aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/common_test/src/ct.erl3
-rw-r--r--lib/common_test/src/ct_telnet.erl6
-rw-r--r--lib/common_test/src/ct_testspec.erl4
-rw-r--r--lib/common_test/src/test_server_node.erl2
-rw-r--r--lib/common_test/src/test_server_sup.erl2
-rw-r--r--lib/debugger/src/dbg_ieval.erl8
-rw-r--r--lib/debugger/src/dbg_wx_break_win.erl4
-rw-r--r--lib/debugger/src/dbg_wx_mon_win.erl4
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl19
-rw-r--r--lib/debugger/src/dbg_wx_win.erl2
-rw-r--r--lib/debugger/src/i.erl8
-rw-r--r--lib/dialyzer/src/dialyzer.erl20
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl2
-rw-r--r--lib/dialyzer/src/typer.erl13
-rw-r--r--lib/diameter/doc/src/diameter.xml39
-rw-r--r--lib/diameter/doc/src/diameter_codec.xml18
-rw-r--r--lib/diameter/doc/src/seealso.ent1
-rw-r--r--lib/diameter/doc/standard/rfc7683.txt2355
-rw-r--r--lib/diameter/src/Makefile6
-rw-r--r--lib/diameter/src/base/diameter.erl3
-rw-r--r--lib/diameter/src/base/diameter_codec.erl6
-rw-r--r--lib/diameter/src/base/diameter_config.erl12
-rw-r--r--lib/diameter/src/base/diameter_gen.erl342
-rw-r--r--lib/diameter/src/base/diameter_reg.erl253
-rw-r--r--lib/diameter/src/base/diameter_service.erl13
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl89
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl4
-rw-r--r--lib/diameter/src/compiler/diameter_dict_util.erl4
-rw-r--r--lib/diameter/src/dict/doic_rfc7683.dia50
-rw-r--r--lib/diameter/src/modules.mk1
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl33
-rw-r--r--lib/diameter/test/diameter_codec_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl2
-rw-r--r--lib/diameter/test/diameter_codec_test.erl5
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl235
-rw-r--r--lib/edoc/src/edoc_doclet.erl2
-rw-r--r--lib/edoc/src/edoc_lib.erl12
-rw-r--r--lib/edoc/src/edoc_run.erl6
-rw-r--r--lib/edoc/src/edoc_types.erl2
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl4
-rw-r--r--lib/erl_docgen/src/docgen_otp_specs.erl6
-rw-r--r--lib/et/src/et_collector.erl34
-rw-r--r--lib/et/src/et_selector.erl8
-rw-r--r--lib/et/src/et_wx_contents_viewer.erl21
-rw-r--r--lib/et/src/et_wx_viewer.erl24
-rw-r--r--lib/eunit/src/eunit_lib.erl22
-rw-r--r--lib/eunit/src/eunit_tty.erl2
-rw-r--r--lib/hipe/cerl/cerl_cconv.erl2
-rw-r--r--lib/kernel/src/error_logger.erl29
-rw-r--r--lib/kernel/src/file_server.erl8
-rw-r--r--lib/kernel/src/global.erl16
-rw-r--r--lib/kernel/src/net_kernel.erl2
-rw-r--r--lib/kernel/src/pg2.erl4
-rw-r--r--lib/kernel/test/Makefile4
-rw-r--r--lib/kernel/test/kernel_bench.spec1
-rw-r--r--lib/kernel/test/zlib_SUITE.erl799
-rw-r--r--lib/mnesia/src/mnesia.erl4
-rw-r--r--lib/mnesia/src/mnesia_bup.erl4
-rw-r--r--lib/mnesia/src/mnesia_checkpoint.erl12
-rw-r--r--lib/mnesia/src/mnesia_controller.erl32
-rw-r--r--lib/mnesia/src/mnesia_dumper.erl10
-rw-r--r--lib/mnesia/src/mnesia_event.erl12
-rw-r--r--lib/mnesia/src/mnesia_index.erl2
-rw-r--r--lib/mnesia/src/mnesia_late_loader.erl4
-rw-r--r--lib/mnesia/src/mnesia_lib.erl22
-rw-r--r--lib/mnesia/src/mnesia_loader.erl30
-rw-r--r--lib/mnesia/src/mnesia_locker.erl4
-rw-r--r--lib/mnesia/src/mnesia_log.erl40
-rw-r--r--lib/mnesia/src/mnesia_monitor.erl28
-rw-r--r--lib/mnesia/src/mnesia_recover.erl8
-rw-r--r--lib/mnesia/src/mnesia_schema.erl40
-rw-r--r--lib/mnesia/src/mnesia_subscr.erl6
-rw-r--r--lib/mnesia/src/mnesia_text.erl14
-rw-r--r--lib/mnesia/src/mnesia_tm.erl32
-rw-r--r--lib/observer/src/cdv_atom_cb.erl2
-rw-r--r--lib/observer/src/cdv_bin_cb.erl3
-rw-r--r--lib/observer/src/cdv_detail_wx.erl2
-rw-r--r--lib/observer/src/cdv_html_wx.erl8
-rw-r--r--lib/observer/src/cdv_info_wx.erl8
-rw-r--r--lib/observer/src/cdv_multi_wx.erl8
-rw-r--r--lib/observer/src/cdv_proc_cb.erl2
-rw-r--r--lib/observer/src/cdv_table_wx.erl8
-rw-r--r--lib/observer/src/cdv_term_cb.erl8
-rw-r--r--lib/observer/src/cdv_virtual_list_wx.erl14
-rw-r--r--lib/observer/src/cdv_wx.erl2
-rw-r--r--lib/observer/src/crashdump_viewer.erl281
-rw-r--r--lib/observer/src/etop_tr.erl4
-rw-r--r--lib/observer/src/etop_txt.erl38
-rw-r--r--lib/observer/src/multitrace.erl14
-rw-r--r--lib/observer/src/observer_alloc_wx.erl4
-rw-r--r--lib/observer/src/observer_app_wx.erl2
-rw-r--r--lib/observer/src/observer_html_lib.erl35
-rw-r--r--lib/observer/src/observer_lib.erl34
-rw-r--r--lib/observer/src/observer_perf_wx.erl4
-rw-r--r--lib/observer/src/observer_port_wx.erl2
-rw-r--r--lib/observer/src/observer_pro_wx.erl12
-rw-r--r--lib/observer/src/observer_procinfo.erl8
-rw-r--r--lib/observer/src/observer_sys_wx.erl8
-rw-r--r--lib/observer/src/observer_trace_wx.erl49
-rw-r--r--lib/observer/src/observer_traceoptions_wx.erl19
-rw-r--r--lib/observer/src/observer_tv_table.erl55
-rw-r--r--lib/observer/src/observer_tv_wx.erl2
-rw-r--r--lib/observer/src/observer_wx.erl4
-rw-r--r--lib/observer/src/ttb.erl69
-rw-r--r--lib/observer/src/ttb_et.erl12
-rw-r--r--lib/observer/test/Makefile5
-rw-r--r--lib/observer/test/crashdump_helper_unicode.erl22
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl25
-rw-r--r--lib/runtime_tools/src/observer_backend.erl31
-rw-r--r--lib/sasl/doc/src/sasl_app.xml9
-rw-r--r--lib/sasl/src/sasl_report.erl81
-rw-r--r--lib/sasl/src/sasl_report_file_h.erl16
-rw-r--r--lib/sasl/test/sasl_report_SUITE.erl40
-rw-r--r--lib/ssl/src/ssl_alert.erl24
-rw-r--r--lib/ssl/src/ssl_connection.erl23
-rw-r--r--lib/ssl/src/ssl_manager.erl2
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl10
-rw-r--r--lib/ssl/test/ssl_alpn_handshake_SUITE.erl10
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl20
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl11
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl9
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl9
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl9
-rw-r--r--lib/ssl/test/ssl_test_lib.erl3
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl9
-rw-r--r--lib/stdlib/src/dets.erl20
-rw-r--r--lib/stdlib/src/dets_utils.erl4
-rw-r--r--lib/stdlib/src/edlin_expand.erl26
-rw-r--r--lib/stdlib/src/error_logger_file_h.erl36
-rw-r--r--lib/stdlib/src/error_logger_tty_h.erl116
-rw-r--r--lib/stdlib/src/escript.erl2
-rw-r--r--lib/stdlib/src/ets.erl2
-rw-r--r--lib/stdlib/src/gen.erl2
-rw-r--r--lib/stdlib/src/gen_event.erl20
-rw-r--r--lib/stdlib/src/gen_fsm.erl40
-rw-r--r--lib/stdlib/src/gen_server.erl28
-rw-r--r--lib/stdlib/src/gen_statem.erl30
-rw-r--r--lib/stdlib/src/proc_lib.erl24
-rw-r--r--lib/stdlib/src/qlc.erl2
-rw-r--r--lib/stdlib/src/shell.erl2
-rw-r--r--lib/stdlib/src/slave.erl4
-rw-r--r--lib/stdlib/src/supervisor.erl4
-rw-r--r--lib/stdlib/test/Makefile1
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE.erl26
-rw-r--r--lib/stdlib/test/error_logger_h_SUITE.erl10
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl33
-rw-r--r--lib/stdlib/test/unicode_expand.erl36
-rw-r--r--lib/syntax_tools/src/epp_dodger.erl4
-rw-r--r--lib/syntax_tools/src/erl_comment_scan.erl2
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl22
-rw-r--r--lib/syntax_tools/src/igor.erl20
-rw-r--r--lib/syntax_tools/src/merl.erl6
-rw-r--r--lib/tools/doc/src/lcnt.xml4
-rw-r--r--lib/tools/emacs/erlang.el1
-rw-r--r--lib/tools/src/eprof.erl30
-rw-r--r--lib/tools/src/fprof.erl16
-rw-r--r--lib/tools/src/xref_base.erl22
-rw-r--r--lib/tools/src/xref_parser.yrl6
-rw-r--r--lib/tools/src/xref_utils.erl13
-rw-r--r--lib/tools/test/xref_SUITE.erl38
-rw-r--r--lib/wx/src/wx_object.erl22
162 files changed, 4881 insertions, 1766 deletions
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 19b0ee20fe..a12c0c9101 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -848,7 +848,8 @@ capture_get([ExclCat | ExclCategories]) ->
Strs = test_server:capture_get(),
CatsStr = [atom_to_list(ExclCat) |
[[$| | atom_to_list(EC)] || EC <- ExclCategories]],
- {ok,MP} = re:compile("<div class=\"(" ++ lists:flatten(CatsStr) ++ ")\">.*"),
+ {ok,MP} = re:compile("<div class=\"(" ++ lists:flatten(CatsStr) ++ ")\">.*",
+ [unicode]),
lists:flatmap(fun(Str) ->
case re:run(Str, MP) of
{match,_} -> [];
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index 14d9d381da..b50cddd492 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -1455,7 +1455,7 @@ match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,Term,
when PromptType=/=FoundPrompt ->
match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) ->
- case re:run(Line,Pattern,[{capture,all,list}]) of
+ case re:run(Line,Pattern,[{capture,all,list},unicode]) of
nomatch ->
match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
@@ -1463,7 +1463,7 @@ match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) ->
{RetTag,{Tag,Match}}
end;
match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,Term,EO,RetTag) ->
- case re:run(Line,Pattern,[{capture,all,list}]) of
+ case re:run(Line,Pattern,[{capture,all,list},unicode]) of
nomatch ->
match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
{match,Match} ->
@@ -1575,7 +1575,7 @@ split_lines([],Line,Lines) ->
match_prompt(Str,Prx) ->
match_prompt(Str,Prx,[]).
match_prompt(Str,Prx,Acc) ->
- case re:run(Str,Prx) of
+ case re:run(Str,Prx,[unicode]) of
nomatch ->
noprompt;
{match,[{Start,Len}]} ->
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 09839bd35d..bb445bb0d2 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -537,7 +537,7 @@ replace_names_in_elems([],Modified,_Defs) ->
replace_names_in_string(Term,Defs=[{Name,Replacement=[Ch|_]}|Ds])
when is_integer(Ch) ->
try re:replace(Term,[$'|atom_to_list(Name)]++"'",
- Replacement,[{return,list}]) of
+ Replacement,[{return,list},unicode]) of
Term -> % no match, proceed
replace_names_in_string(Term,Ds);
Term1 ->
@@ -569,7 +569,7 @@ replace_names_in_node1(NodeStr,Defs=[{Name,Replacement}|Ds]) ->
replace_names_in_node1(NodeStr,Ds);
true ->
case re:replace(NodeStr,atom_to_list(Name),
- ReplStr,[{return,list}]) of
+ ReplStr,[{return,list},unicode]) of
NodeStr -> % no match, proceed
replace_names_in_node1(NodeStr,Ds);
NodeStr1 ->
diff --git a/lib/common_test/src/test_server_node.erl b/lib/common_test/src/test_server_node.erl
index a18ff1fd62..c0d7e12721 100644
--- a/lib/common_test/src/test_server_node.erl
+++ b/lib/common_test/src/test_server_node.erl
@@ -692,7 +692,7 @@ find_rel_suse_2(Rel, RootWc) ->
case file:list_dir(RelDir) of
{ok,Dirs} ->
case lists:filter(fun(Dir) ->
- case re:run(Dir, Pat) of
+ case re:run(Dir, Pat, [unicode]) of
nomatch -> false;
_ -> true
end
diff --git a/lib/common_test/src/test_server_sup.erl b/lib/common_test/src/test_server_sup.erl
index 9a26de4774..21f4be22fe 100644
--- a/lib/common_test/src/test_server_sup.erl
+++ b/lib/common_test/src/test_server_sup.erl
@@ -346,7 +346,7 @@ check_appup_clauses_plausible([], _Direction, _Modules) ->
ok;
check_appup_clauses_plausible([{Re, Instrs} | Rest], Direction, Modules)
when is_binary(Re) ->
- case re:compile(Re) of
+ case re:compile(Re,[unicode]) of
{ok, _} ->
case check_appup_instructions(Instrs, Direction, Modules) of
ok ->
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index 88c7caacb0..8009d62629 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -353,15 +353,15 @@ format_trace(What, Args, P) ->
{Called, {Le,Li,M,F,As}} = Args,
case Called of
extern ->
- io_lib:format("++ (~w) <~w> ~w:~w~ts~n",
+ io_lib:format("++ (~w) <~w> ~w:~tw~ts~n",
[Le,Li,M,F,format_args(As, P)]);
local ->
- io_lib:format("++ (~w) <~w> ~w~ts~n",
+ io_lib:format("++ (~w) <~w> ~tw~ts~n",
[Le,Li,F,format_args(As, P)])
end;
call_fun ->
{Le,Li,F,As} = Args,
- io_lib:format("++ (~w) <~w> ~w~ts~n",
+ io_lib:format("++ (~w) <~w> ~tw~ts~n",
[Le, Li, F, format_args(As, P)]);
return ->
{Le,Val} = Args,
@@ -370,7 +370,7 @@ format_trace(What, Args, P) ->
bif ->
{Le,Li,M,F,As} = Args,
- io_lib:format("++ (~w) <~w> ~w:~w~ts~n",
+ io_lib:format("++ (~w) <~w> ~w:~tw~ts~n",
[Le, Li, M, F, format_args(As, P)])
end.
diff --git a/lib/debugger/src/dbg_wx_break_win.erl b/lib/debugger/src/dbg_wx_break_win.erl
index 770681510d..10e9272254 100644
--- a/lib/debugger/src/dbg_wx_break_win.erl
+++ b/lib/debugger/src/dbg_wx_break_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -159,7 +159,7 @@ create_win(Parent, Pos, Type, Mod, Line) ->
%%--------------------------------------------------------------------
update_functions(WinInfo, Funcs) ->
Items = lists:map(fun([N, A]) ->
- lists:flatten(io_lib:format("~p/~p", [N,A]))
+ lists:flatten(io_lib:format("~tw/~w", [N,A]))
end,
Funcs),
wxListBox:set(WinInfo#winInfo.listbox, Items),
diff --git a/lib/debugger/src/dbg_wx_mon_win.erl b/lib/debugger/src/dbg_wx_mon_win.erl
index 9737c9e67f..fcd954454b 100644
--- a/lib/debugger/src/dbg_wx_mon_win.erl
+++ b/lib/debugger/src/dbg_wx_mon_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -342,7 +342,7 @@ add_process(WinInfo, Pid, Name, {Mod,Func,Args}, Status, Info) ->
Row = (WinInfo#winInfo.row),
Name2 = case Name of undefined -> ""; _ -> to_string(Name) end,
- FuncS = to_string("~w:~w/~w", [Mod, Func, length(Args)]),
+ FuncS = to_string("~w:~tw/~w", [Mod, Func, length(Args)]),
Info2 = case Info of {} -> ""; _ -> to_string(Info) end,
Pid2 = to_string("~p",[Pid]),
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index f4ee30618c..b1e0e03b4c 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.erl
@@ -345,11 +345,12 @@ gui_cmd('Back Trace', State) ->
P = p(State),
lists:foreach(
fun({Le, {Mod,Func,Args}}) ->
- Str = io_lib:format("~p > ~p:~p"++P++"~n",
- [Le, Mod, Func, Args]),
+ Str = io_lib:format("~p > ~w:~tw~ts\n",
+ [Le, Mod, Func, format_args(Args, P)]),
dbg_wx_trace_win:trace_output(State#state.win,Str);
({Le, {Fun,Args}}) ->
- Str = io_lib:format("~p > ~p"++P++"~n", [Le, Fun, Args]),
+ Str = io_lib:format("~p > ~p~ts~n",
+ [Le, Fun, format_args(Args, P)]),
dbg_wx_trace_win:trace_output(State#state.win,Str);
(_) -> ignore
end,
@@ -539,6 +540,18 @@ add_break(WI, Coords, Type, Mod, Line) ->
Win = dbg_wx_trace_win:get_window(WI),
dbg_wx_break:start(Win, Coords, Type, Mod, Line).
+format_args(As, P) when is_list(As) ->
+ [$(,format_args1(As, P),$)];
+format_args(A, P) ->
+ [$/,io_lib:format(P, [A])].
+
+format_args1([A], P) ->
+ [io_lib:format(P, [A])];
+format_args1([A|As], P) ->
+ [io_lib:format(P, [A]),$,|format_args1(As, P)];
+format_args1([], _) ->
+ [].
+
%%--Commands from the interpreter-------------------------------------
int_cmd({interpret, Mod}, State) ->
diff --git a/lib/debugger/src/dbg_wx_win.erl b/lib/debugger/src/dbg_wx_win.erl
index f1298154ab..9f59915476 100644
--- a/lib/debugger/src/dbg_wx_win.erl
+++ b/lib/debugger/src/dbg_wx_win.erl
@@ -299,7 +299,7 @@ open_help(_Parent, HelpHtmlFile) ->
%%--------------------------------------------------------------------
to_string(Atom) when is_atom(Atom) ->
- atom_to_list(Atom);
+ io_lib:format("~tw", [Atom]);
to_string(Integer) when is_integer(Integer) ->
integer_to_list(Integer);
to_string([]) -> "";
diff --git a/lib/debugger/src/i.erl b/lib/debugger/src/i.erl
index 2da3e77618..62ce8d0e20 100644
--- a/lib/debugger/src/i.erl
+++ b/lib/debugger/src/i.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -307,13 +307,13 @@ ip() ->
ip([{Pid,{M,F,A},Status,{}}|Stats]) ->
hformat(io_lib:format("~w",[Pid]),
- io_lib:format("~p:~p/~p",[M,F,length(A)]),
+ io_lib:format("~w:~tw/~w",[M,F,length(A)]),
io_lib:format("~w",[Status]),
""),
ip(Stats);
ip([{Pid,{M,F,A},Status,Info}|Stats]) ->
hformat(io_lib:format("~w",[Pid]),
- io_lib:format("~p:~p/~p",[M,F,length(A)]),
+ io_lib:format("~w:~tw/~w",[M,F,length(A)]),
io_lib:format("~w",[Status]),
io_lib:format("~w",[Info])),
ip(Stats);
@@ -321,7 +321,7 @@ ip([]) ->
ok.
hformat(A1, A2, A3, A4) ->
- format("~-12s ~-21s ~-9s ~-21s~n", [A1,A2,A3,A4]).
+ format("~-12s ~-21ts ~-9s ~-21s~n", [A1,A2,A3,A4]).
%% -------------------------------------------
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index c319acb2fb..1538174d4a 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -498,24 +498,24 @@ call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet,
true ->
%% We do not know which argument(s) caused the failure
io_lib:format("will never return since the success typing arguments"
- " are ~s\n", [SigArgs]);
+ " are ~ts\n", [SigArgs]);
false ->
io_lib:format("will never return since it differs in the ~s argument"
- " from the success typing arguments: ~s\n",
+ " from the success typing arguments: ~ts\n",
[PositionString, SigArgs])
end;
only_contract ->
case (ArgNs =:= []) orelse IsOverloaded of
true ->
%% We do not know which arguments caused the failure
- io_lib:format("breaks the contract ~s\n", [Contract]);
+ io_lib:format("breaks the contract ~ts\n", [Contract]);
false ->
- io_lib:format("breaks the contract ~s in the ~s argument\n",
+ io_lib:format("breaks the contract ~ts in the ~s argument\n",
[Contract, PositionString])
end;
both ->
- io_lib:format("will never return since the success typing is ~s -> ~s"
- " and the contract is ~s\n", [SigArgs, SigRet, Contract])
+ io_lib:format("will never return since the success typing is ~ts -> ~ts"
+ " and the contract is ~ts\n", [SigArgs, SigRet, Contract])
end.
form_positions(ArgNs) ->
@@ -533,9 +533,9 @@ form_positions(ArgNs) ->
form_expected_without_opaque([{N, T, TStr}]) ->
case erl_types:t_is_opaque(T) of
true ->
- io_lib:format("an opaque term of type ~s as ", [TStr]);
+ io_lib:format("an opaque term of type ~ts as ", [TStr]);
false ->
- io_lib:format("a term of type ~s (with opaque subterms) as ", [TStr])
+ io_lib:format("a term of type ~ts (with opaque subterms) as ", [TStr])
end ++ form_position_string([N]) ++ " argument";
form_expected_without_opaque(ExpectedTriples) -> %% TODO: can do much better here
{ArgNs, _Ts, _TStrs} = lists:unzip3(ExpectedTriples),
@@ -546,8 +546,8 @@ form_expected(ExpectedArgs) ->
[T] ->
TS = erl_types:t_to_string(T),
case erl_types:t_is_opaque(T) of
- true -> io_lib:format("an opaque term of type ~s is expected", [TS]);
- false -> io_lib:format("a structured term of type ~s is expected", [TS])
+ true -> io_lib:format("an opaque term of type ~ts is expected", [TS]);
+ false -> io_lib:format("a structured term of type ~ts is expected", [TS])
end;
[_,_|_] -> "terms of different types are expected in these positions"
end.
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index a456d38e64..80c10183cf 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -82,7 +82,7 @@ cl(["--get_warnings"|T]) ->
cl(["-D"|_]) ->
cl_error("No defines specified after -D");
cl(["-D"++Define|T]) ->
- Def = re:split(Define, "=", [{return, list}]),
+ Def = re:split(Define, "=", [{return, list}, unicode]),
append_defines(Def),
cl(T);
cl(["-h"|_]) ->
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index 538327d4d1..b4b1872c12 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -1093,7 +1093,7 @@ macro_loop(Options, Win, Box, MacroText, TermText, Frame) ->
Fun =
fun(X) ->
Val = wxControlWithItems:getString(Box,X),
- [MacroName|_] = re:split(Val, " ", [{return, list}]),
+ [MacroName|_] = re:split(Val, " ", [{return, list}, unicode]),
list_to_atom(MacroName)
end,
Delete = [Fun(X) || X <- List],
diff --git a/lib/dialyzer/src/typer.erl b/lib/dialyzer/src/typer.erl
index bf5484e5f6..16b9c8a94a 100644
--- a/lib/dialyzer/src/typer.erl
+++ b/lib/dialyzer/src/typer.erl
@@ -74,7 +74,8 @@
-spec start() -> no_return().
start() ->
-_ = io:setopts(standard_error, [{encoding,unicode}]),
+ _ = io:setopts(standard_error, [{encoding,unicode}]),
+ _ = io:setopts([{encoding,unicode}]),
{Args, Analysis} = process_cl_args(),
%% io:format("Args: ~p\n", [Args]),
%% io:format("Analysis: ~p\n", [Analysis]),
@@ -484,12 +485,12 @@ write_typed_file(File, Info) ->
write_typed_file(File, Info, NewFileName) ->
{ok, Binary} = file:read_file(File),
- Chars = binary_to_list(Binary),
+ Chars = unicode:characters_to_list(Binary),
write_typed_file(Chars, NewFileName, Info, 1, []),
io:format(" Saved as: ~tp\n", [NewFileName]).
write_typed_file(Chars, File, #info{functions = []}, _LNo, _Acc) ->
- ok = file:write_file(File, list_to_binary(Chars), [append]);
+ ok = file:write_file(File, unicode:characters_to_binary(Chars), [append]);
write_typed_file([Ch|Chs] = Chars, File, Info, LineNo, Acc) ->
[{Line,F,A}|RestFuncs] = Info#info.functions,
case Line of
@@ -519,7 +520,7 @@ write_typed_file([Ch|Chs] = Chars, File, Info, LineNo, Acc) ->
raw_write(F, A, Info, File, Content) ->
TypeInfo = get_type_string(F, A, Info, file),
ContentList = lists:reverse(Content) ++ TypeInfo ++ "\n",
- ContentBin = list_to_binary(ContentList),
+ ContentBin = unicode:characters_to_binary(ContentList),
file:write_file(File, ContentBin, [append]).
get_type_string(F, A, Info, Mode) ->
@@ -608,7 +609,7 @@ cl(["-D"++Def|Opts]) ->
case Def of
"" -> fatal_error("no variable name specified after -D");
_ ->
- DefPair = process_def_list(re:split(Def, "=", [{return, list}])),
+ DefPair = process_def_list(re:split(Def, "=", [{return, list}, unicode])),
{{def, DefPair}, Opts}
end;
cl(["-I",Dir|Opts]) -> {{inc, Dir}, Opts};
@@ -697,7 +698,7 @@ get_all_files(#args{files = Fs, files_r = Ds}) ->
test_erl_file_exclude_ann(File) ->
case is_erl_file(File) of
true -> %% Exclude files ending with ".ann.erl"
- case re:run(File, "[\.]ann[\.]erl$") of
+ case re:run(File, "[\.]ann[\.]erl$", [unicode]) of
{match, _} -> false;
nomatch -> true
end;
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index b7be184058..6b84b22eb5 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -799,7 +799,7 @@ be matched by corresponding &capability; configuration, of
<tag>
<marker id="decode_format"/>
-<c>{decode_format, record | list | map | false}</c></tag>
+<c>{decode_format, record | list | map | none}</c></tag>
<item>
<p>
The format of decoded messages and grouped AVPs in the <c>msg</c> field
@@ -808,10 +808,10 @@ records respectively.
If <c>record</c> then a record whose definition is generated from the
dictionary file in question.
If <c>list</c> or <c>map</c> then a <c>[Name | Avps]</c> pair where
-<c>Avps</c> is either a list of AVP name/values pairs or a map keyed on
+<c>Avps</c> is a list of AVP name/values pairs or a map keyed on
AVP names respectively.
-If <c>false</c> then the representation is omitted and <c>msg</c> and
-<c>value</c> fields are set to <c>false</c>.
+If <c>none</c> then the atom-value message name, or <c>undefined</c>
+for a Grouped AVP.
See also &codec_message;.</p>
<p>
@@ -1083,6 +1083,37 @@ implies having to set matching *-Application-Id AVPs in a
</item>
<tag>
+<marker id="avp_dictionaries"/><c>{avp_dictionaries, [module()]}</c></tag>
+<item>
+<p>
+A list of alternate dictionary modules with which to encode/decode
+AVPs that are not defined by the dictionary of the application in
+question.
+At decode, such AVPs are represented as diameter_avp records in the
+<c>'AVP'</c> field of a decoded message or Grouped AVP, the first
+alternate that succeeds in decoding the AVP setting the record's value
+field.
+At encode, values in an <c>'AVP'</c> list can be passed as AVP
+name/value 2-tuples, and it is an encode error for no alternate to
+define the AVP of such a tuple.</p>
+
+<p>
+Defaults to the empty list.</p>
+
+<note>
+<p>
+The motivation for alternate dictionaries is RFC 7683, Diameter
+Overload Indication Conveyance (DOIC), which defines AVPs to
+be piggybacked onto existing application messages rather than defining
+an application of its own.
+The DOIC dictionary is provided by the diameter application, as module
+<c>diameter_gen_doic_rfc7683</c>, but alternate dictionaries can be
+used to encode/decode any set of AVPs not known to an application
+dictionary.</p>
+</note>
+</item>
+
+<tag>
<marker id="capabilities"/><c>{capabilities, [&capability;]}</c></tag>
<item>
<p>
diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml
index 0846334d23..5124b49484 100644
--- a/lib/diameter/doc/src/diameter_codec.xml
+++ b/lib/diameter/doc/src/diameter_codec.xml
@@ -4,7 +4,10 @@
'<seealso marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>'>
<!ENTITY types
'<seealso marker="diameter_dict#DATA_TYPES">diameter_dict(4)</seealso>'>
- <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY decode_format
+ '<seealso marker="diameter#decode_format">decode format</seealso>'>
+
+<!ENTITY % also SYSTEM "seealso.ent" >
<!ENTITY % here SYSTEM "seehere.ent" >
%also;
%here;
@@ -145,7 +148,8 @@ question.</p>
<p>
The decoded value of an AVP.
Will be <c>undefined</c> on decode if the data bytes could
-not be decoded or the AVP is unknown.
+not be decoded, the AVP is unknown, or if the &decode_format; is
+<c>none</c>.
The type of a decoded value is as document in &types;.</p>
</item>
@@ -243,8 +247,7 @@ Equivalently, a message can also be encoded as a list whose head is
the atom-valued message name (as specified in the relevant dictionary
file) and whose tail is either a list of AVP name/values
pairs or a map with values keyed on AVP names.
-The format at decode is determined by &mod_service_opt;
-<c>decode_format</c>.
+The format at decode is determined by &mod_decode_format;.
Any of the formats is accepted at encode.</p>
<p>
@@ -288,15 +291,16 @@ value other than <c>undefined</c>.</p>
<item>
<p>
The incoming/outgoing message.
-For an incoming message, a record if the message can be
-decoded in a non-relay application, <c>undefined</c> otherwise.
+For an incoming message, a term corresponding to the configured
+&decode_format; if the message can be decoded in a non-relay
+application, <c>undefined</c> otherwise.
For an outgoing message, setting a <c>[&header; | &avp;]</c> list is
equivalent to setting the <c>header</c> and <c>avps</c> fields to the
corresponding values.</p>
<warning>
<p>
-A record-valued <c>msg</c> field does <em>not</em> imply an absence of
+A value in the <c>msg</c> field does <em>not</em> imply an absence of
decode errors.
The <c>errors</c> field should also be examined.</p>
</warning>
diff --git a/lib/diameter/doc/src/seealso.ent b/lib/diameter/doc/src/seealso.ent
index ef6af1a3d0..c5a53670d0 100644
--- a/lib/diameter/doc/src/seealso.ent
+++ b/lib/diameter/doc/src/seealso.ent
@@ -72,6 +72,7 @@ significant.
<!ENTITY watchdog_timer '<seealso marker="#watchdog_timer">watchdog_timer</seealso>'>
<!ENTITY mod_string_decode '<seealso marker="diameter#service_opt">diameter:service_opt()</seealso> <seealso marker="diameter#string_decode">string_decode</seealso>'>
+<!ENTITY mod_decode_format '<seealso marker="diameter#service_opt">diameter:service_opt()</seealso> <seealso marker="diameter#decode_format">decode_format</seealso>'>
<!-- diameter_app -->
diff --git a/lib/diameter/doc/standard/rfc7683.txt b/lib/diameter/doc/standard/rfc7683.txt
new file mode 100644
index 0000000000..ab2392c6c0
--- /dev/null
+++ b/lib/diameter/doc/standard/rfc7683.txt
@@ -0,0 +1,2355 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF) J. Korhonen, Ed.
+Request for Comments: 7683 Broadcom Corporation
+Category: Standards Track S. Donovan, Ed.
+ISSN: 2070-1721 B. Campbell
+ Oracle
+ L. Morand
+ Orange Labs
+ October 2015
+
+
+ Diameter Overload Indication Conveyance
+
+Abstract
+
+ This specification defines a base solution for Diameter overload
+ control, referred to as Diameter Overload Indication Conveyance
+ (DOIC).
+
+Status of This Memo
+
+ This is an Internet Standards Track document.
+
+ This document is a product of the Internet Engineering Task Force
+ (IETF). It represents the consensus of the IETF community. It has
+ received public review and has been approved for publication by the
+ Internet Engineering Steering Group (IESG). Further information on
+ Internet Standards is available in Section 2 of RFC 5741.
+
+ Information about the current status of this document, any errata,
+ and how to provide feedback on it may be obtained at
+ http://www.rfc-editor.org/info/rfc7683.
+
+Copyright Notice
+
+ Copyright (c) 2015 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 1]
+
+RFC 7683 DOIC October 2015
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2. Terminology and Abbreviations . . . . . . . . . . . . . . . . 3
+ 3. Conventions Used in This Document . . . . . . . . . . . . . . 5
+ 4. Solution Overview . . . . . . . . . . . . . . . . . . . . . . 5
+ 4.1. Piggybacking . . . . . . . . . . . . . . . . . . . . . . 6
+ 4.2. DOIC Capability Announcement . . . . . . . . . . . . . . 7
+ 4.3. DOIC Overload Condition Reporting . . . . . . . . . . . . 9
+ 4.4. DOIC Extensibility . . . . . . . . . . . . . . . . . . . 11
+ 4.5. Simplified Example Architecture . . . . . . . . . . . . . 12
+ 5. Solution Procedures . . . . . . . . . . . . . . . . . . . . . 12
+ 5.1. Capability Announcement . . . . . . . . . . . . . . . . . 12
+ 5.1.1. Reacting Node Behavior . . . . . . . . . . . . . . . 13
+ 5.1.2. Reporting Node Behavior . . . . . . . . . . . . . . . 13
+ 5.1.3. Agent Behavior . . . . . . . . . . . . . . . . . . . 14
+ 5.2. Overload Report Processing . . . . . . . . . . . . . . . 15
+ 5.2.1. Overload Control State . . . . . . . . . . . . . . . 15
+ 5.2.2. Reacting Node Behavior . . . . . . . . . . . . . . . 19
+ 5.2.3. Reporting Node Behavior . . . . . . . . . . . . . . . 20
+ 5.3. Protocol Extensibility . . . . . . . . . . . . . . . . . 22
+ 6. Loss Algorithm . . . . . . . . . . . . . . . . . . . . . . . 23
+ 6.1. Overview . . . . . . . . . . . . . . . . . . . . . . . . 23
+ 6.2. Reporting Node Behavior . . . . . . . . . . . . . . . . . 24
+ 6.3. Reacting Node Behavior . . . . . . . . . . . . . . . . . 24
+ 7. Attribute Value Pairs . . . . . . . . . . . . . . . . . . . . 25
+ 7.1. OC-Supported-Features AVP . . . . . . . . . . . . . . . . 25
+ 7.2. OC-Feature-Vector AVP . . . . . . . . . . . . . . . . . . 25
+ 7.3. OC-OLR AVP . . . . . . . . . . . . . . . . . . . . . . . 26
+ 7.4. OC-Sequence-Number AVP . . . . . . . . . . . . . . . . . 26
+ 7.5. OC-Validity-Duration AVP . . . . . . . . . . . . . . . . 26
+ 7.6. OC-Report-Type AVP . . . . . . . . . . . . . . . . . . . 27
+ 7.7. OC-Reduction-Percentage AVP . . . . . . . . . . . . . . . 27
+ 7.8. AVP Flag Rules . . . . . . . . . . . . . . . . . . . . . 28
+ 8. Error Response Codes . . . . . . . . . . . . . . . . . . . . 28
+ 9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 29
+ 9.1. AVP Codes . . . . . . . . . . . . . . . . . . . . . . . . 29
+ 9.2. New Registries . . . . . . . . . . . . . . . . . . . . . 29
+ 10. Security Considerations . . . . . . . . . . . . . . . . . . . 30
+ 10.1. Potential Threat Modes . . . . . . . . . . . . . . . . . 30
+ 10.2. Denial-of-Service Attacks . . . . . . . . . . . . . . . 31
+ 10.3. Noncompliant Nodes . . . . . . . . . . . . . . . . . . . 32
+ 10.4. End-to-End Security Issues . . . . . . . . . . . . . . . 32
+ 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 34
+ 11.1. Normative References . . . . . . . . . . . . . . . . . . 34
+ 11.2. Informative References . . . . . . . . . . . . . . . . . 34
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 2]
+
+RFC 7683 DOIC October 2015
+
+
+ Appendix A. Issues Left for Future Specifications . . . . . . . 35
+ A.1. Additional Traffic Abatement Algorithms . . . . . . . . . 35
+ A.2. Agent Overload . . . . . . . . . . . . . . . . . . . . . 35
+ A.3. New Error Diagnostic AVP . . . . . . . . . . . . . . . . 35
+ Appendix B. Deployment Considerations . . . . . . . . . . . . . 35
+ Appendix C. Considerations for Applications Integrating the DOIC
+ Solution . . . . . . . . . . . . . . . . . . . . . . 36
+ C.1. Application Classification . . . . . . . . . . . . . . . 36
+ C.2. Implications of Application Type Overload . . . . . . . . 37
+ C.3. Request Transaction Classification . . . . . . . . . . . 38
+ C.4. Request Type Overload Implications . . . . . . . . . . . 39
+ Contributors . . . . . . . . . . . . . . . . . . . . . . . . . . 41
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 42
+
+1. Introduction
+
+ This specification defines a base solution for Diameter overload
+ control, referred to as Diameter Overload Indication Conveyance
+ (DOIC), based on the requirements identified in [RFC7068].
+
+ This specification addresses Diameter overload control between
+ Diameter nodes that support the DOIC solution. The solution, which
+ is designed to apply to existing and future Diameter applications,
+ requires no changes to the Diameter base protocol [RFC6733] and is
+ deployable in environments where some Diameter nodes do not implement
+ the Diameter overload control solution defined in this specification.
+
+ A new application specification can incorporate the overload control
+ mechanism specified in this document by making it mandatory to
+ implement for the application and referencing this specification
+ normatively. It is the responsibility of the Diameter application
+ designers to define how overload control mechanisms work on that
+ application.
+
+ Note that the overload control solution defined in this specification
+ does not address all the requirements listed in [RFC7068]. A number
+ of features related to overload control are left for future
+ specifications. See Appendix A for a list of extensions that are
+ currently being considered.
+
+2. Terminology and Abbreviations
+
+ Abatement
+
+ Reaction to receipt of an overload report resulting in a reduction
+ in traffic sent to the reporting node. Abatement actions include
+ diversion and throttling.
+
+
+
+
+Korhonen, et al. Standards Track [Page 3]
+
+RFC 7683 DOIC October 2015
+
+
+ Abatement Algorithm
+
+ An extensible method requested by reporting nodes and used by
+ reacting nodes to reduce the amount of traffic sent during an
+ occurrence of overload control.
+
+ Diversion
+
+ An overload abatement treatment where the reacting node selects
+ alternate destinations or paths for requests.
+
+ Host-Routed Requests
+
+ Requests that a reacting node knows will be served by a particular
+ host, either due to the presence of a Destination-Host Attribute
+ Value Pair (AVP) or by some other local knowledge on the part of
+ the reacting node.
+
+ Overload Control State (OCS)
+
+ Internal state maintained by a reporting or reacting node
+ describing occurrences of overload control.
+
+ Overload Report (OLR)
+
+ Overload control information for a particular overload occurrence
+ sent by a reporting node.
+
+ Reacting Node
+
+ A Diameter node that acts upon an overload report.
+
+ Realm-Routed Requests
+
+ Requests sent by a reacting node where the reacting node does not
+ know to which host the request will be routed.
+
+ Reporting Node
+
+ A Diameter node that generates an overload report. (This may or
+ may not be the overloaded node.)
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 4]
+
+RFC 7683 DOIC October 2015
+
+
+ Throttling
+
+ An abatement treatment that limits the number of requests sent by
+ the reacting node. Throttling can include a Diameter Client
+ choosing to not send requests, or a Diameter Agent or Server
+ rejecting requests with appropriate error responses. In both
+ cases, the result of the throttling is a permanent rejection of
+ the transaction.
+
+3. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+ The interpretation from RFC 2119 [RFC2119] does not apply for the
+ above listed words when they are not used in all caps.
+
+4. Solution Overview
+
+ The Diameter Overload Information Conveyance (DOIC) solution allows
+ Diameter nodes to request that other Diameter nodes perform overload
+ abatement actions, that is, actions to reduce the load offered to the
+ overloaded node or realm.
+
+ A Diameter node that supports DOIC is known as a "DOIC node". Any
+ Diameter node can act as a DOIC node, including Diameter Clients,
+ Diameter Servers, and Diameter Agents. DOIC nodes are further
+ divided into "Reporting Nodes" and "Reacting Nodes." A reporting
+ node requests overload abatement by sending Overload Reports (OLRs).
+
+ A reacting node acts upon OLRs and performs whatever actions are
+ needed to fulfill the abatement requests included in the OLRs. A
+ reporting node may report overload on its own behalf or on behalf of
+ other nodes. Likewise, a reacting node may perform overload
+ abatement on its own behalf or on behalf of other nodes.
+
+ A Diameter node's role as a DOIC node is independent of its Diameter
+ role. For example, Diameter Agents may act as DOIC nodes, even
+ though they are not endpoints in the Diameter sense. Since Diameter
+ enables bidirectional applications, where Diameter Servers can send
+ requests towards Diameter Clients, a given Diameter node can
+ simultaneously act as both a reporting node and a reacting node.
+
+ Likewise, a Diameter Agent may act as a reacting node from the
+ perspective of upstream nodes, and a reporting node from the
+ perspective of downstream nodes.
+
+
+
+
+Korhonen, et al. Standards Track [Page 5]
+
+RFC 7683 DOIC October 2015
+
+
+ DOIC nodes do not generate new messages to carry DOIC-related
+ information. Rather, they "piggyback" DOIC information over existing
+ Diameter messages by inserting new AVPs into existing Diameter
+ requests and responses. Nodes indicate support for DOIC, and any
+ needed DOIC parameters, by inserting an OC-Supported-Features AVP
+ (Section 7.1) into existing requests and responses. Reporting nodes
+ send OLRs by inserting OC-OLR AVPs (Section 7.3).
+
+ A given OLR applies to the Diameter realm and application of the
+ Diameter message that carries it. If a reporting node supports more
+ than one realm and/or application, it reports independently for each
+ combination of realm and application. Similarly, the OC-Supported-
+ Features AVP applies to the realm and application of the enclosing
+ message. This implies that a node may support DOIC for one
+ application and/or realm, but not another, and may indicate different
+ DOIC parameters for each application and realm for which it supports
+ DOIC.
+
+ Reacting nodes perform overload abatement according to an agreed-upon
+ abatement algorithm. An abatement algorithm defines the meaning of
+ some of the parameters of an OLR and the procedures required for
+ overload abatement. An overload abatement algorithm separates
+ Diameter requests into two sets. The first set contains the requests
+ that are to undergo overload abatement treatment of either throttling
+ or diversion. The second set contains the requests that are to be
+ given normal routing treatment. This document specifies a single
+ "must-support" algorithm, namely, the "loss" algorithm (Section 6).
+ Future specifications may introduce new algorithms.
+
+ Overload conditions may vary in scope. For example, a single
+ Diameter node may be overloaded, in which case, reacting nodes may
+ attempt to send requests to other destinations. On the other hand,
+ an entire Diameter realm may be overloaded, in which case, such
+ attempts would do harm. DOIC OLRs have a concept of "report type"
+ (Section 7.6), where the type defines such behaviors. Report types
+ are extensible. This document defines report types for overload of a
+ specific host and for overload of an entire realm.
+
+ DOIC works through non-supporting Diameter Agents that properly pass
+ unknown AVPs unchanged.
+
+4.1. Piggybacking
+
+ There is no new Diameter application defined to carry overload-
+ related AVPs. The overload control AVPs defined in this
+ specification have been designed to be piggybacked on top of existing
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 6]
+
+RFC 7683 DOIC October 2015
+
+
+ application messages. This is made possible by adding the optional
+ overload control AVPs OC-OLR and OC-Supported-Features into existing
+ commands.
+
+ Reacting nodes indicate support for DOIC by including the
+ OC-Supported-Features AVP in all request messages originated or
+ relayed by the reacting node.
+
+ Reporting nodes indicate support for DOIC by including the
+ OC-Supported-Features AVP in all answer messages that are originated
+ or relayed by the reporting node and that are in response to a
+ request that contained the OC-Supported-Features AVP. Reporting
+ nodes may include overload reports using the OC-OLR AVP in answer
+ messages.
+
+ Note that the overload control solution does not have fixed server
+ and client roles. The DOIC node role is determined based on the
+ message type: whether the message is a request (i.e., sent by a
+ "reacting node") or an answer (i.e., sent by a "reporting node").
+ Therefore, in a typical client-server deployment, the Diameter Client
+ may report its overload condition to the Diameter Server for any
+ Diameter-Server-initiated message exchange. An example of such is
+ the Diameter Server requesting a re-authentication from a Diameter
+ Client.
+
+4.2. DOIC Capability Announcement
+
+ The DOIC solution supports the ability for Diameter nodes to
+ determine if other nodes in the path of a request support the
+ solution. This capability is referred to as DOIC Capability
+ Announcement (DCA) and is separate from the Diameter Capability
+ Exchange.
+
+ The DCA mechanism uses the OC-Supported-Features AVPs to indicate the
+ Diameter overload features supported.
+
+ The first node in the path of a Diameter request that supports the
+ DOIC solution inserts the OC-Supported-Features AVP in the request
+ message.
+
+ The individual features supported by the DOIC nodes are indicated in
+ the OC-Feature-Vector AVP. Any semantics associated with the
+ features will be defined in extension specifications that introduce
+ the features.
+
+ Note: As discussed elsewhere in the document, agents in the path
+ of the request can modify the OC-Supported-Features AVP.
+
+
+
+
+Korhonen, et al. Standards Track [Page 7]
+
+RFC 7683 DOIC October 2015
+
+
+ Note: The DOIC solution must support deployments where Diameter
+ Clients and/or Diameter Servers do not support the DOIC solution.
+ In this scenario, Diameter Agents that support the DOIC solution
+ may handle overload abatement for the non-supporting Diameter
+ nodes. In this case, the DOIC agent will insert the OC-Supported-
+ Features AVP in requests that do not already contain one, telling
+ the reporting node that there is a DOIC node that will handle
+ overload abatement. For transactions where there was an
+ OC-Supporting-Features AVP in the request, the agent will insert
+ the OC-Supported-Features AVP in answers, telling the reacting
+ node that there is a reporting node.
+
+ The OC-Feature-Vector AVP will always contain an indication of
+ support for the loss overload abatement algorithm defined in this
+ specification (see Section 6). This ensures that a reporting node
+ always supports at least one of the advertised abatement algorithms
+ received in a request messages.
+
+ The reporting node inserts the OC-Supported-Features AVP in all
+ answer messages to requests that contained the OC-Supported-Features
+ AVP. The contents of the reporting node's OC-Supported-Features AVP
+ indicate the set of Diameter overload features supported by the
+ reporting node. This specification defines one exception -- the
+ reporting node only includes an indication of support for one
+ overload abatement algorithm, independent of the number of overload
+ abatement algorithms actually supported by the reacting node. The
+ overload abatement algorithm indicated is the algorithm that the
+ reporting node intends to use should it enter an overload condition.
+ Reacting nodes can use the indicated overload abatement algorithm to
+ prepare for possible overload reports and must use the indicated
+ overload abatement algorithm if traffic reduction is actually
+ requested.
+
+ Note that the loss algorithm defined in this document is a
+ stateless abatement algorithm. As a result, it does not require
+ any actions by reacting nodes prior to the receipt of an overload
+ report. Stateful abatement algorithms that base the abatement
+ logic on a history of request messages sent might require reacting
+ nodes to maintain state in advance of receiving an overload report
+ to ensure that the overload reports can be properly handled.
+
+ While it should only be done in exceptional circumstances and not
+ during an active occurrence of overload, a reacting node that wishes
+ to transition to a different abatement algorithm can stop advertising
+ support for the algorithm indicated by the reporting node, as long as
+ support for the loss algorithm is always advertised.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 8]
+
+RFC 7683 DOIC October 2015
+
+
+ The DCA mechanism must also allow the scenario where the set of
+ features supported by the sender of a request and by agents in the
+ path of a request differ. In this case, the agent can update the
+ OC-Supported-Features AVP to reflect the mixture of the two sets of
+ supported features.
+
+ Note: The logic to determine if the content of the OC-Supported-
+ Features AVP should be changed is out of scope for this document,
+ as is the logic to determine the content of a modified
+ OC-Supported-Features AVP. These are left to implementation
+ decisions. Care must be taken not to introduce interoperability
+ issues for downstream or upstream DOIC nodes. As such, the agent
+ must act as a fully compliant reporting node to the downstream
+ reacting node and as a fully compliant reacting node to the
+ upstream reporting node.
+
+4.3. DOIC Overload Condition Reporting
+
+ As with DOIC capability announcement, overload condition reporting
+ uses new AVPs (Section 7.3) to indicate an overload condition.
+
+ The OC-OLR AVP is referred to as an overload report. The OC-OLR AVP
+ includes the type of report, a sequence number, the length of time
+ that the report is valid, and AVPs specific to the abatement
+ algorithm.
+
+ Two types of overload reports are defined in this document: host
+ reports and realm reports.
+
+ A report of type "HOST_REPORT" is sent to indicate the overload of a
+ specific host, identified by the Origin-Host AVP of the message
+ containing the OLR, for the Application-ID indicated in the
+ transaction. When receiving an OLR of type "HOST_REPORT", a reacting
+ node applies overload abatement treatment to the host-routed requests
+ identified by the overload abatement algorithm (as defined in
+ Section 2) sent for this application to the overloaded host.
+
+ A report of type "REALM_REPORT" is sent to indicate the overload of a
+ realm for the Application-ID indicated in the transaction. The
+ overloaded realm is identified by the Destination-Realm AVP of the
+ message containing the OLR. When receiving an OLR of type
+ "REALM_REPORT", a reacting node applies overload abatement treatment
+ to realm-routed requests identified by the overload abatement
+ algorithm (as defined in Section 2) sent for this application to the
+ overloaded realm.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 9]
+
+RFC 7683 DOIC October 2015
+
+
+ This document assumes that there is a single source for realm reports
+ for a given realm, or that if multiple nodes can send realm reports,
+ that each such node has full knowledge of the overload state of the
+ entire realm. A reacting node cannot distinguish between receiving
+ realm reports from a single node or from multiple nodes.
+
+ Note: Known issues exist if there are multiple sources for
+ overload reports that apply to the same Diameter entity. Reacting
+ nodes have no way of determining the source and, as such, will
+ treat them as coming from a single source. Variance in sequence
+ numbers between the two sources can then cause incorrect overload
+ abatement treatment to be applied for indeterminate periods of
+ time.
+
+ Reporting nodes are responsible for determining the need for a
+ reduction of traffic. The method for making this determination is
+ implementation specific and depends on the type of overload report
+ being generated. A host report might be generated by tracking use of
+ resources required by the host to handle transactions for the
+ Diameter application. A realm report generally impacts the traffic
+ sent to multiple hosts and, as such, requires tracking the capacity
+ of all servers able to handle realm-routed requests for the
+ application and realm.
+
+ Once a reporting node determines the need for a reduction in traffic,
+ it uses the DOIC-defined AVPs to report on the condition. These AVPs
+ are included in answer messages sent or relayed by the reporting
+ node. The reporting node indicates the overload abatement algorithm
+ that is to be used to handle the traffic reduction in the
+ OC-Supported-Features AVP. The OC-OLR AVP is used to communicate
+ information about the requested reduction.
+
+ Reacting nodes, upon receipt of an overload report, apply the
+ overload abatement algorithm to traffic impacted by the overload
+ report. The method used to determine the requests that are to
+ receive overload abatement treatment is dependent on the abatement
+ algorithm. The loss abatement algorithm is defined in this document
+ (Section 6). Other abatement algorithms can be defined in extensions
+ to the DOIC solution.
+
+ Two types of overload abatement treatment are defined, diversion and
+ throttling. Reacting nodes are responsible for determining which
+ treatment is appropriate for individual requests.
+
+ As the conditions that lead to the generation of the overload report
+ change, the reporting node can send new overload reports requesting
+ greater reduction if the condition gets worse or less reduction if
+ the condition improves. The reporting node sends an overload report
+
+
+
+Korhonen, et al. Standards Track [Page 10]
+
+RFC 7683 DOIC October 2015
+
+
+ with a duration of zero to indicate that the overload condition has
+ ended and abatement is no longer needed.
+
+ The reacting node also determines when the overload report expires
+ based on the OC-Validity-Duration AVP in the overload report and
+ stops applying the abatement algorithm when the report expires.
+
+ Note that erroneous overload reports can be used for DoS attacks.
+ This includes the ability to indicate that a significant reduction in
+ traffic, up to and including a request for no traffic, should be sent
+ to a reporting node. As such, care should be taken to verify the
+ sender of overload reports.
+
+4.4. DOIC Extensibility
+
+ The DOIC solution is designed to be extensible. This extensibility
+ is based on existing Diameter-based extensibility mechanisms, along
+ with the DOIC capability announcement mechanism.
+
+ There are multiple categories of extensions that are expected. This
+ includes the definition of new overload abatement algorithms, the
+ definition of new report types, and the definition of new scopes of
+ messages impacted by an overload report.
+
+ A DOIC node communicates supported features by including them in the
+ OC-Feature-Vector AVP, as a sub-AVP of OC-Supported-Features. Any
+ non-backwards-compatible DOIC extensions define new values for the
+ OC-Feature-Vector AVP. DOIC extensions also have the ability to add
+ new AVPs to the OC-Supported-Features AVP, if additional information
+ about the new feature is required.
+
+ Overload reports can also be extended by adding new sub-AVPs to the
+ OC-OLR AVP, allowing reporting nodes to communicate additional
+ information about handling an overload condition.
+
+ If necessary, new extensions can also define new AVPs that are not
+ part of the OC-Supported-Features and OC-OLR group AVPs. It is,
+ however, recommended that DOIC extensions use the OC-Supported-
+ Features AVP and OC-OLR AVP to carry all DOIC-related AVPs.
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 11]
+
+RFC 7683 DOIC October 2015
+
+
+4.5. Simplified Example Architecture
+
+ Figure 1 illustrates the simplified architecture for Diameter
+ overload information conveyance.
+
+ Realm X Same or other Realms
+ <--------------------------------------> <---------------------->
+
+
+ +--------+ : (optional) :
+ |Diameter| : :
+ |Server A|--+ .--. : +--------+ : .--.
+ +--------+ | _( `. : |Diameter| : _( `. +--------+
+ +--( )--:-| Agent |-:--( )--|Diameter|
+ +--------+ | ( ` . ) ) : +--------+ : ( ` . ) ) | Client |
+ |Diameter|--+ `--(___.-' : : `--(___.-' +--------+
+ |Server B| : :
+ +--------+ : :
+
+ End-to-end Overload Indication
+ 1) <----------------------------------------------->
+ Diameter Application Y
+
+ Overload Indication A Overload Indication A'
+ 2) <----------------------> <---------------------->
+ Diameter Application Y Diameter Application Y
+
+ Figure 1: Simplified Architecture Choices for Overload Indication
+ Delivery
+
+ In Figure 1, the Diameter overload indication can be conveyed (1)
+ end-to-end between servers and clients or (2) between servers and the
+ Diameter Agent inside the realm and then between the Diameter Agent
+ and the clients.
+
+5. Solution Procedures
+
+ This section outlines the normative behavior for the DOIC solution.
+
+5.1. Capability Announcement
+
+ This section defines DOIC Capability Announcement (DCA) behavior.
+
+ Note: This specification assumes that changes in DOIC node
+ capabilities are relatively rare events that occur as a result of
+ administrative action. Reacting nodes ought to minimize changes
+ that force the reporting node to change the features being used,
+ especially during active overload conditions. But even if
+
+
+
+Korhonen, et al. Standards Track [Page 12]
+
+RFC 7683 DOIC October 2015
+
+
+ reacting nodes avoid such changes, reporting nodes still have to
+ be prepared for them to occur. For example, differing
+ capabilities between multiple reacting nodes may still force a
+ reporting node to select different features on a per-transaction
+ basis.
+
+5.1.1. Reacting Node Behavior
+
+ A reacting node MUST include the OC-Supported-Features AVP in all
+ requests. It MAY include the OC-Feature-Vector AVP, as a sub-AVP of
+ OC-Supported-Features. If it does so, it MUST indicate support for
+ the "loss" algorithm. If the reacting node is configured to support
+ features (including other algorithms) in addition to the loss
+ algorithm, it MUST indicate such support in an OC-Feature-Vector AVP.
+
+ An OC-Supported-Features AVP in answer messages indicates there is a
+ reporting node for the transaction. The reacting node MAY take
+ action, for example, creating state for some stateful abatement
+ algorithm, based on the features indicated in the OC-Feature-Vector
+ AVP.
+
+ Note: The loss abatement algorithm does not require stateful
+ behavior when there is no active overload report.
+
+ Reacting nodes need to be prepared for the reporting node to change
+ selected algorithms. This can happen at any time, including when the
+ reporting node has sent an active overload report. The reacting node
+ can minimize the potential for changes by modifying the advertised
+ abatement algorithms sent to an overloaded reporting node to the
+ currently selected algorithm and loss (or just loss if it is the
+ currently selected algorithm). This has the effect of limiting the
+ potential change in abatement algorithm from the currently selected
+ algorithm to loss, avoiding changes to more complex abatement
+ algorithms that require state to operate properly.
+
+5.1.2. Reporting Node Behavior
+
+ Upon receipt of a request message, a reporting node determines if
+ there is a reacting node for the transaction based on the presence of
+ the OC-Supported-Features AVP in the request message.
+
+ If the request message contains an OC-Supported-Features AVP, then a
+ reporting node MUST include the OC-Supported-Features AVP in the
+ answer message for that transaction.
+
+ Note: Capability announcement is done on a per-transaction basis.
+ The reporting node cannot assume that the capabilities announced
+ by a reacting node will be the same between transactions.
+
+
+
+Korhonen, et al. Standards Track [Page 13]
+
+RFC 7683 DOIC October 2015
+
+
+ A reporting node MUST NOT include the OC-Supported-Features AVP,
+ OC-OLR AVP, or any other overload control AVPs defined in extension
+ documents in response messages for transactions where the request
+ message does not include the OC-Supported-Features AVP. Lack of the
+ OC-Supported-Features AVP in the request message indicates that there
+ is no reacting node for the transaction.
+
+ A reporting node knows what overload control functionality is
+ supported by the reacting node based on the content or absence of the
+ OC-Feature-Vector AVP within the OC-Supported-Features AVP in the
+ request message.
+
+ A reporting node MUST select a single abatement algorithm in the
+ OC-Feature-Vector AVP. The abatement algorithm selected MUST
+ indicate the abatement algorithm the reporting node wants the
+ reacting node to use when the reporting node enters an overload
+ condition.
+
+ The abatement algorithm selected MUST be from the set of abatement
+ algorithms contained in the request message's OC-Feature-Vector AVP.
+
+ A reporting node that selects the loss algorithm may do so by
+ including the OC-Feature-Vector AVP with an explicit indication of
+ the loss algorithm, or it MAY omit the OC-Feature-Vector AVP. If it
+ selects a different algorithm, it MUST include the OC-Feature-Vector
+ AVP with an explicit indication of the selected algorithm.
+
+ The reporting node SHOULD indicate support for other DOIC features
+ defined in extension documents that it supports and that apply to the
+ transaction. It does so using the OC-Feature-Vector AVP.
+
+ Note: Not all DOIC features will apply to all Diameter
+ applications or deployment scenarios. The features included in
+ the OC-Feature-Vector AVP are based on local policy of the
+ reporting node.
+
+5.1.3. Agent Behavior
+
+ Diameter Agents that support DOIC can ensure that all messages
+ relayed by the agent contain the OC-Supported-Features AVP.
+
+ A Diameter Agent MAY take on reacting node behavior for Diameter
+ endpoints that do not support the DOIC solution. A Diameter Agent
+ detects that a Diameter endpoint does not support DOIC reacting node
+ behavior when there is no OC-Supported-Features AVP in a request
+ message.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 14]
+
+RFC 7683 DOIC October 2015
+
+
+ For a Diameter Agent to be a reacting node for a non-supporting
+ Diameter endpoint, the Diameter Agent MUST include the OC-Supported-
+ Features AVP in request messages it relays that do not contain the
+ OC-Supported-Features AVP.
+
+ A Diameter Agent MAY take on reporting node behavior for Diameter
+ endpoints that do not support the DOIC solution. The Diameter Agent
+ MUST have visibility to all traffic destined for the non-supporting
+ host in order to become the reporting node for the Diameter endpoint.
+ A Diameter Agent detects that a Diameter endpoint does not support
+ DOIC reporting node behavior when there is no OC-Supported-Features
+ AVP in an answer message for a transaction that contained the
+ OC-Supported-Features AVP in the request message.
+
+ If a request already has the OC-Supported-Features AVP, a Diameter
+ Agent MAY modify it to reflect the features appropriate for the
+ transaction. Otherwise, the agent relays the OC-Supported-Features
+ AVP without change.
+
+ Example: If the agent supports a superset of the features reported
+ by the reacting node, then the agent might choose, based on local
+ policy, to advertise that superset of features to the reporting
+ node.
+
+ If the Diameter Agent changes the OC-Supported-Features AVP in a
+ request message, then it is likely it will also need to modify the
+ OC-Supported-Features AVP in the answer message for the transaction.
+ A Diameter Agent MAY modify the OC-Supported-Features AVP carried in
+ answer messages.
+
+ When making changes to the OC-Supported-Features or OC-OLR AVPs, the
+ Diameter Agent needs to ensure consistency in its behavior with both
+ upstream and downstream DOIC nodes.
+
+5.2. Overload Report Processing
+
+5.2.1. Overload Control State
+
+ Both reacting and reporting nodes maintain Overload Control State
+ (OCS) for active overload conditions. The following sections define
+ behavior associated with that OCS.
+
+ The contents of the OCS in the reporting node and in the reacting
+ node represent logical constructs. The actual internal physical
+ structure of the state included in the OCS is an implementation
+ decision.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 15]
+
+RFC 7683 DOIC October 2015
+
+
+5.2.1.1. Overload Control State for Reacting Nodes
+
+ A reacting node maintains the following OCS per supported Diameter
+ application:
+
+ o a host-type OCS entry for each Destination-Host to which it sends
+ host-type requests and
+
+ o a realm-type OCS entry for each Destination-Realm to which it
+ sends realm-type requests.
+
+ A host-type OCS entry is identified by the pair of Application-ID and
+ the node's DiameterIdentity.
+
+ A realm-type OCS entry is identified by the pair of Application-ID
+ and realm.
+
+ The host-type and realm-type OCS entries include the following
+ information (the actual information stored is an implementation
+ decision):
+
+ o Sequence number (as received in OC-OLR; see Section 7.3)
+
+ o Time of expiry (derived from OC-Validity-Duration AVP received in
+ the OC-OLR AVP and time of reception of the message carrying
+ OC-OLR AVP)
+
+ o Selected abatement algorithm (as received in the OC-Supported-
+ Features AVP)
+
+ o Input data that is abatement algorithm specific (as received in
+ the OC-OLR AVP -- for example, OC-Reduction-Percentage for the
+ loss abatement algorithm)
+
+5.2.1.2. Overload Control State for Reporting Nodes
+
+ A reporting node maintains OCS entries per supported Diameter
+ application, per supported (and eventually selected) abatement
+ algorithm, and per report type.
+
+ An OCS entry is identified by the tuple of Application-ID, report
+ type, and abatement algorithm, and it includes the following
+ information (the actual information stored is an implementation
+ decision):
+
+ o Sequence number
+
+ o Validity duration
+
+
+
+Korhonen, et al. Standards Track [Page 16]
+
+RFC 7683 DOIC October 2015
+
+
+ o Expiration time
+
+ o Input data that is algorithm specific (for example, the reduction
+ percentage for the loss abatement algorithm)
+
+5.2.1.3. Reacting Node's Maintenance of Overload Control State
+
+ When a reacting node receives an OC-OLR AVP, it MUST determine if it
+ is for an existing or new overload condition.
+
+ Note: For the remainder of this section, the term "OLR" refers to
+ the combination of the contents of the received OC-OLR AVP and the
+ abatement algorithm indicated in the received OC-Supported-
+ Features AVP.
+
+ When receiving an answer message with multiple OLRs of different
+ supported report types, a reacting node MUST process each received
+ OLR.
+
+ The OLR is for an existing overload condition if a reacting node has
+ an OCS that matches the received OLR.
+
+ For a host report, this means it matches the Application-ID and the
+ host's DiameterIdentity in an existing host OCS entry.
+
+ For a realm report, this means it matches the Application-ID and the
+ realm in an existing realm OCS entry.
+
+ If the OLR is for an existing overload condition, then a reacting
+ node MUST determine if the OLR is a retransmission or an update to
+ the existing OLR.
+
+ If the sequence number for the received OLR is greater than the
+ sequence number stored in the matching OCS entry, then a reacting
+ node MUST update the matching OCS entry.
+
+ If the sequence number for the received OLR is less than or equal to
+ the sequence number in the matching OCS entry, then a reacting node
+ MUST silently ignore the received OLR. The matching OCS MUST NOT be
+ updated in this case.
+
+ If the reacting node determines that the sequence number has rolled
+ over, then the reacting node MUST update the matching OCS entry.
+ This can be determined by recognizing that the number has changed
+ from a value within 1% of the maximum value in the OC-Sequence-Number
+ AVP to a value within 1% of the minimum value in the OC-Sequence-
+ Number AVP.
+
+
+
+
+Korhonen, et al. Standards Track [Page 17]
+
+RFC 7683 DOIC October 2015
+
+
+ If the received OLR is for a new overload condition, then a reacting
+ node MUST generate a new OCS entry for the overload condition.
+
+ For a host report, this means a reacting node creates an OCS entry
+ with the Application-ID in the received message and DiameterIdentity
+ of the Origin-Host in the received message.
+
+ Note: This solution assumes that the Origin-Host AVP in the answer
+ message included by the reporting node is not changed along the
+ path to the reacting node.
+
+ For a realm report, this means a reacting node creates an OCS entry
+ with the Application-ID in the received message and realm of the
+ Origin-Realm in the received message.
+
+ If the received OLR contains a validity duration of zero ("0"), then
+ a reacting node MUST update the OCS entry as being expired.
+
+ Note: It is not necessarily appropriate to delete the OCS entry,
+ as the recommended behavior is that the reacting node slowly
+ returns to full traffic when ending an overload abatement period.
+
+ The reacting node does not delete an OCS when receiving an answer
+ message that does not contain an OC-OLR AVP (i.e., absence of OLR
+ means "no change").
+
+5.2.1.4. Reporting Node's Maintenance of Overload Control State
+
+ A reporting node SHOULD create a new OCS entry when entering an
+ overload condition.
+
+ Note: If a reporting node knows through absence of the
+ OC-Supported-Features AVP in received messages that there are no
+ reacting nodes supporting DOIC, then the reporting node can choose
+ to not create OCS entries.
+
+ When generating a new OCS entry, the sequence number SHOULD be set to
+ zero ("0").
+
+ When generating sequence numbers for new overload conditions, the new
+ sequence number MUST be greater than any sequence number in an active
+ (unexpired) overload report for the same application and report type
+ previously sent by the reporting node. This property MUST hold over
+ a reboot of the reporting node.
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 18]
+
+RFC 7683 DOIC October 2015
+
+
+ Note: One way of addressing this over a reboot of a reporting node
+ is to use a timestamp for the first overload condition that occurs
+ after the report and to start using sequences beginning with zero
+ for subsequent overload conditions.
+
+ A reporting node MUST update an OCS entry when it needs to adjust the
+ validity duration of the overload condition at reacting nodes.
+
+ Example: If a reporting node wishes to instruct reacting nodes to
+ continue overload abatement for a longer period of time than
+ originally communicated. This also applies if the reporting node
+ wishes to shorten the period of time that overload abatement is to
+ continue.
+
+ A reporting node MUST update an OCS entry when it wishes to adjust
+ any parameters specific to the abatement algorithm, including, for
+ example, the reduction percentage used for the loss abatement
+ algorithm.
+
+ Example: If a reporting node wishes to change the reduction
+ percentage either higher (if the overload condition has worsened)
+ or lower (if the overload condition has improved), then the
+ reporting node would update the appropriate OCS entry.
+
+ A reporting node MUST increment the sequence number associated with
+ the OCS entry anytime the contents of the OCS entry are changed.
+ This will result in a new sequence number being sent to reacting
+ nodes, instructing them to process the OC-OLR AVP.
+
+ A reporting node SHOULD update an OCS entry with a validity duration
+ of zero ("0") when the overload condition ends.
+
+ Note: If a reporting node knows that the OCS entries in the
+ reacting nodes are near expiration, then the reporting node might
+ decide not to send an OLR with a validity duration of zero.
+
+ A reporting node MUST keep an OCS entry with a validity duration of
+ zero ("0") for a period of time long enough to ensure that any
+ unexpired reacting node's OCS entry created as a result of the
+ overload condition in the reporting node is deleted.
+
+5.2.2. Reacting Node Behavior
+
+ When a reacting node sends a request, it MUST determine if that
+ request matches an active OCS.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 19]
+
+RFC 7683 DOIC October 2015
+
+
+ If the request matches an active OCS, then the reacting node MUST use
+ the overload abatement algorithm indicated in the OCS to determine if
+ the request is to receive overload abatement treatment.
+
+ For the loss abatement algorithm defined in this specification, see
+ Section 6 for the overload abatement algorithm logic applied.
+
+ If the overload abatement algorithm selects the request for overload
+ abatement treatment, then the reacting node MUST apply overload
+ abatement treatment on the request. The abatement treatment applied
+ depends on the context of the request.
+
+ If diversion abatement treatment is possible (i.e., a different path
+ for the request can be selected where the overloaded node is not part
+ of the different path), then the reacting node SHOULD apply diversion
+ abatement treatment to the request. The reacting node MUST apply
+ throttling abatement treatment to requests identified for abatement
+ treatment when diversion treatment is not possible or was not
+ applied.
+
+ Note: This only addresses the case where there are two defined
+ abatement treatments, diversion and throttling. Any extension
+ that defines a new abatement treatment must also define its
+ interaction with existing treatments.
+
+ If the overload abatement treatment results in throttling of the
+ request and if the reacting node is an agent, then the agent MUST
+ send an appropriate error as defined in Section 8.
+
+ Diameter endpoints that throttle requests need to do so according to
+ the rules of the client application. Those rules will vary by
+ application and are beyond the scope of this document.
+
+ In the case that the OCS entry indicated no traffic was to be sent to
+ the overloaded entity and the validity duration expires, then
+ overload abatement associated with the overload report MUST be ended
+ in a controlled fashion.
+
+5.2.3. Reporting Node Behavior
+
+ If there is an active OCS entry, then a reporting node SHOULD include
+ the OC-OLR AVP in all answers to requests that contain the
+ OC-Supported-Features AVP and that match the active OCS entry.
+
+ Note: A request matches 1) if the Application-ID in the request
+ matches the Application-ID in any active OCS entry and 2) if the
+ report type in the OCS entry matches a report type supported by
+ the reporting node as indicated in the OC-Supported-Features AVP.
+
+
+
+Korhonen, et al. Standards Track [Page 20]
+
+RFC 7683 DOIC October 2015
+
+
+ The contents of the OC-OLR AVP depend on the selected algorithm.
+
+ A reporting node MAY choose to not resend an overload report to a
+ reacting node if it can guarantee that this overload report is
+ already active in the reacting node.
+
+ Note: In some cases (e.g., when there are one or more agents in
+ the path between reporting and reacting nodes, or when overload
+ reports are discarded by reacting nodes), a reporting node may not
+ be able to guarantee that the reacting node has received the
+ report.
+
+ A reporting node MUST NOT send overload reports of a type that has
+ not been advertised as supported by the reacting node.
+
+ Note: A reacting node implicitly advertises support for the host
+ and realm report types by including the OC-Supported-Features AVP
+ in the request. Support for other report types will be explicitly
+ indicated by new feature bits in the OC-Feature-Vector AVP.
+
+ A reporting node SHOULD explicitly indicate the end of an overload
+ occurrence by sending a new OLR with OC-Validity-Duration set to a
+ value of zero ("0"). The reporting node SHOULD ensure that all
+ reacting nodes receive the updated overload report.
+
+ A reporting node MAY rely on the OC-Validity-Duration AVP values for
+ the implicit cleanup of overload control state on the reacting node.
+
+ Note: All OLRs sent have an expiration time calculated by adding
+ the validity duration contained in the OLR to the time the message
+ was sent. Transit time for the OLR can be safely ignored. The
+ reporting node can ensure that all reacting nodes have received
+ the OLR by continuing to send it in answer messages until the
+ expiration time for all OLRs sent for that overload condition have
+ expired.
+
+ When a reporting node sends an OLR, it effectively delegates any
+ necessary throttling to downstream nodes. If the reporting node also
+ locally throttles the same set of messages, the overall number of
+ throttled requests may be higher than intended. Therefore, before
+ applying local message throttling, a reporting node needs to check if
+ these messages match existing OCS entries, indicating that these
+ messages have survived throttling applied by downstream nodes that
+ have received the related OLR.
+
+ However, even if the set of messages match existing OCS entries, the
+ reporting node can still apply other abatement methods such as
+ diversion. The reporting node might also need to throttle requests
+
+
+
+Korhonen, et al. Standards Track [Page 21]
+
+RFC 7683 DOIC October 2015
+
+
+ for reasons other than overload. For example, an agent or server
+ might have a configured rate limit for each client and might throttle
+ requests that exceed that limit, even if such requests had already
+ been candidates for throttling by downstream nodes. The reporting
+ node also has the option to send new OLRs requesting greater
+ reductions in traffic, reducing the need for local throttling.
+
+ A reporting node SHOULD decrease requested overload abatement
+ treatment in a controlled fashion to avoid oscillations in traffic.
+
+ Example: A reporting node might wait some period of time after
+ overload ends before terminating the OLR, or it might send a
+ series of OLRs indicating progressively less overload severity.
+
+5.3. Protocol Extensibility
+
+ The DOIC solution can be extended. Types of potential extensions
+ include new traffic abatement algorithms, new report types, or other
+ new functionality.
+
+ When defining a new extension that requires new normative behavior,
+ the specification must define a new feature for the OC-Feature-Vector
+ AVP. This feature bit is used to communicate support for the new
+ feature.
+
+ The extension may define new AVPs for use in the DOIC Capability
+ Announcement and for use in DOIC overload reporting. These new AVPs
+ SHOULD be defined to be extensions to the OC-Supported-Features or
+ OC-OLR AVPs defined in this document.
+
+ The Grouped AVP extension mechanisms defined in [RFC6733] apply.
+ This allows, for example, defining a new feature that is mandatory to
+ be understood even when piggybacked on an existing application.
+
+ When defining new report type values, the corresponding specification
+ must define the semantics of the new report types and how they affect
+ the OC-OLR AVP handling.
+
+ The OC-Supported-Feature and OC-OLR AVPs can be expanded with
+ optional sub-AVPs only if a legacy DOIC implementation can safely
+ ignore them without breaking backward compatibility for the given
+ OC-Report-Type AVP value. Any new sub-AVPs must not require that the
+ M-bit be set.
+
+ Documents that introduce new report types must describe any
+ limitations on their use across non-supporting agents.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 22]
+
+RFC 7683 DOIC October 2015
+
+
+ As with any Diameter specification, RFC 6733 requires all new AVPs to
+ be registered with IANA. See Section 9 for the required procedures.
+ New features (feature bits in the OC-Feature-Vector AVP) and report
+ types (in the OC-Report-Type AVP) MUST be registered with IANA.
+
+6. Loss Algorithm
+
+ This section documents the Diameter overload loss abatement
+ algorithm.
+
+6.1. Overview
+
+ The DOIC specification supports the ability for multiple overload
+ abatement algorithms to be specified. The abatement algorithm used
+ for any instance of overload is determined by the DOIC Capability
+ Announcement process documented in Section 5.1.
+
+ The loss algorithm described in this section is the default algorithm
+ that must be supported by all Diameter nodes that support DOIC.
+
+ The loss algorithm is designed to be a straightforward and stateless
+ overload abatement algorithm. It is used by reporting nodes to
+ request a percentage reduction in the amount of traffic sent. The
+ traffic impacted by the requested reduction depends on the type of
+ overload report.
+
+ Reporting nodes request the stateless reduction of the number of
+ requests by an indicated percentage. This percentage reduction is in
+ comparison to the number of messages the node otherwise would send,
+ regardless of how many requests the node might have sent in the past.
+
+ From a conceptual level, the logic at the reacting node could be
+ outlined as follows.
+
+ 1. An overload report is received, and the associated OCS is either
+ saved or updated (if required) by the reacting node.
+
+ 2. A new Diameter request is generated by the application running on
+ the reacting node.
+
+ 3. The reacting node determines that an active overload report
+ applies to the request, as indicated by the corresponding OCS
+ entry.
+
+ 4. The reacting node determines if overload abatement treatment
+ should be applied to the request. One approach that could be
+ taken for each request is to select a uniformly selected random
+ number between 1 and 100. If the random number is less than or
+
+
+
+Korhonen, et al. Standards Track [Page 23]
+
+RFC 7683 DOIC October 2015
+
+
+ equal to the indicated reduction percentage, then the request is
+ given abatement treatment; otherwise, the request is given normal
+ routing treatment.
+
+6.2. Reporting Node Behavior
+
+ The method a reporting node uses to determine the amount of traffic
+ reduction required to address an overload condition is an
+ implementation decision.
+
+ When a reporting node that has selected the loss abatement algorithm
+ determines the need to request a reduction in traffic, it includes an
+ OC-OLR AVP in answer messages as described in Section 5.2.3.
+
+ When sending the OC-OLR AVP, the reporting node MUST indicate a
+ percentage reduction in the OC-Reduction-Percentage AVP.
+
+ The reporting node MAY change the reduction percentage in subsequent
+ overload reports. When doing so, the reporting node must conform to
+ overload report handling specified in Section 5.2.3.
+
+6.3. Reacting Node Behavior
+
+ The method a reacting node uses to determine which request messages
+ are given abatement treatment is an implementation decision.
+
+ When receiving an OC-OLR in an answer message where the algorithm
+ indicated in the OC-Supported-Features AVP is the loss algorithm, the
+ reacting node MUST apply abatement treatment to the requested
+ percentage of request messages sent.
+
+ Note: The loss algorithm is a stateless algorithm. As a result,
+ the reacting node does not guarantee that there will be an
+ absolute reduction in traffic sent. Rather, it guarantees that
+ the requested percentage of new requests will be given abatement
+ treatment.
+
+ If the reacting node comes out of the 100% traffic reduction
+ (meaning, it has received an OLR indicating that no traffic should be
+ sent, as a result of the overload report timing out), the reacting
+ node sending the traffic SHOULD be conservative and, for example,
+ first send "probe" messages to learn the overload condition of the
+ overloaded node before converging to any traffic amount/rate decided
+ by the sender. Similar concerns apply in all cases when the overload
+ report times out, unless the previous overload report stated 0%
+ reduction.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 24]
+
+RFC 7683 DOIC October 2015
+
+
+ Note: The goal of this behavior is to reduce the probability of
+ overload condition thrashing where an immediate transition from
+ 100% reduction to 0% reduction results in the reporting node
+ moving quickly back into an overload condition.
+
+7. Attribute Value Pairs
+
+ This section describes the encoding and semantics of the Diameter
+ Overload Indication Attribute Value Pairs (AVPs) defined in this
+ document.
+
+ Refer to Section 4 of [RFC6733] for more information on AVPs and AVP
+ data types.
+
+7.1. OC-Supported-Features AVP
+
+ The OC-Supported-Features AVP (AVP Code 621) is of type Grouped and
+ serves two purposes. First, it announces a node's support for the
+ DOIC solution in general. Second, it contains the description of the
+ supported DOIC features of the sending node. The OC-Supported-
+ Features AVP MUST be included in every Diameter request message a
+ DOIC supporting node sends.
+
+ OC-Supported-Features ::= < AVP Header: 621 >
+ [ OC-Feature-Vector ]
+ * [ AVP ]
+
+7.2. OC-Feature-Vector AVP
+
+ The OC-Feature-Vector AVP (AVP Code 622) is of type Unsigned64 and
+ contains a 64-bit flags field of announced capabilities of a DOIC
+ node. The value of zero (0) is reserved.
+
+ The OC-Feature-Vector sub-AVP is used to announce the DOIC features
+ supported by the DOIC node, in the form of a flag-bits field in which
+ each bit announces one feature or capability supported by the node.
+ The absence of the OC-Feature-Vector AVP in request messages
+ indicates that only the default traffic abatement algorithm described
+ in this specification is supported. The absence of the OC-Feature-
+ Vector AVP in answer messages indicates that the default traffic
+ abatement algorithm described in this specification is selected
+ (while other traffic abatement algorithms may be supported), and no
+ features other than abatement algorithms are supported.
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 25]
+
+RFC 7683 DOIC October 2015
+
+
+ The following capability is defined in this document:
+
+ OLR_DEFAULT_ALGO (0x0000000000000001)
+
+ When this flag is set by the a DOIC reacting node, it means that
+ the default traffic abatement (loss) algorithm is supported. When
+ this flag is set by a DOIC reporting node, it means that the loss
+ algorithm will be used for requested overload abatement.
+
+7.3. OC-OLR AVP
+
+ The OC-OLR AVP (AVP Code 623) is of type Grouped and contains the
+ information necessary to convey an overload report on an overload
+ condition at the reporting node. The application the OC-OLR AVP
+ applies to is identified by the Application-ID found in the Diameter
+ message header. The host or realm the OC-OLR AVP concerns is
+ determined from the Origin-Host AVP and/or Origin-Realm AVP found in
+ the encapsulating Diameter command. The OC-OLR AVP is intended to be
+ sent only by a reporting node.
+
+ OC-OLR ::= < AVP Header: 623 >
+ < OC-Sequence-Number >
+ < OC-Report-Type >
+ [ OC-Reduction-Percentage ]
+ [ OC-Validity-Duration ]
+ * [ AVP ]
+
+7.4. OC-Sequence-Number AVP
+
+ The OC-Sequence-Number AVP (AVP Code 624) is of type Unsigned64. Its
+ usage in the context of overload control is described in Section 5.2.
+
+ From the functionality point of view, the OC-Sequence-Number AVP is
+ used as a nonvolatile increasing counter for a sequence of overload
+ reports between two DOIC nodes for the same overload occurrence.
+ Sequence numbers are treated in a unidirectional manner, i.e., two
+ sequence numbers in each direction between two DOIC nodes are not
+ related or correlated.
+
+7.5. OC-Validity-Duration AVP
+
+ The OC-Validity-Duration AVP (AVP Code 625) is of type Unsigned32 and
+ indicates in seconds the validity time of the overload report. The
+ number of seconds is measured after reception of the first OC-OLR AVP
+ with a given value of OC-Sequence-Number AVP. The default value for
+ the OC-Validity-Duration AVP is 30 seconds. When the OC-Validity-
+ Duration AVP is not present in the OC-OLR AVP, the default value
+ applies. The maximum value for the OC-Validity-Duration AVP is
+
+
+
+Korhonen, et al. Standards Track [Page 26]
+
+RFC 7683 DOIC October 2015
+
+
+ 86,400 seconds (24 hours). If the value received in the OC-Validity-
+ Duration is greater than the maximum value, then the default value
+ applies.
+
+7.6. OC-Report-Type AVP
+
+ The OC-Report-Type AVP (AVP Code 626) is of type Enumerated. The
+ value of the AVP describes what the overload report concerns. The
+ following values are initially defined:
+
+ HOST_REPORT 0
+ The overload report is for a host. Overload abatement treatment
+ applies to host-routed requests.
+
+ REALM_REPORT 1
+ The overload report is for a realm. Overload abatement treatment
+ applies to realm-routed requests.
+
+ The values 2-4294967295 are unassigned.
+
+7.7. OC-Reduction-Percentage AVP
+
+ The OC-Reduction-Percentage AVP (AVP Code 627) is of type Unsigned32
+ and describes the percentage of the traffic that the sender is
+ requested to reduce, compared to what it otherwise would send. The
+ OC-Reduction-Percentage AVP applies to the default (loss) algorithm
+ specified in this specification. However, the AVP can be reused for
+ future abatement algorithms, if its semantics fit into the new
+ algorithm.
+
+ The value of the Reduction-Percentage AVP is between zero (0) and one
+ hundred (100). Values greater than 100 are ignored. The value of
+ 100 means that all traffic is to be throttled, i.e., the reporting
+ node is under a severe load and ceases to process any new messages.
+ The value of 0 means that the reporting node is in a stable state and
+ has no need for the reacting node to apply any traffic abatement.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 27]
+
+RFC 7683 DOIC October 2015
+
+
+7.8. AVP Flag Rules
+
+ +---------+
+ |AVP flag |
+ |rules |
+ +----+----+
+ AVP Section | |MUST|
+ Attribute Name Code Defined Value Type |MUST| NOT|
+ +--------------------------------------------------+----+----+
+ |OC-Supported-Features 621 7.1 Grouped | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Feature-Vector 622 7.2 Unsigned64 | | V |
+ +--------------------------------------------------+----+----+
+ |OC-OLR 623 7.3 Grouped | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Sequence-Number 624 7.4 Unsigned64 | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Validity-Duration 625 7.5 Unsigned32 | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Report-Type 626 7.6 Enumerated | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Reduction | | |
+ | -Percentage 627 7.7 Unsigned32 | | V |
+ +--------------------------------------------------+----+----+
+
+ As described in the Diameter base protocol [RFC6733], the M-bit usage
+ for a given AVP in a given command may be defined by the application.
+
+8. Error Response Codes
+
+ When a DOIC node rejects a Diameter request due to overload, the DOIC
+ node MUST select an appropriate error response code. This
+ determination is made based on the probability of the request
+ succeeding if retried on a different path.
+
+ Note: This only applies for DOIC nodes that are not the originator
+ of the request.
+
+ A reporting node rejecting a Diameter request due to an overload
+ condition SHOULD send a DIAMETER_TOO_BUSY error response, if it can
+ assume that the same request may succeed on a different path.
+
+ If a reporting node knows or assumes that the same request will not
+ succeed on a different path, the DIAMETER_UNABLE_TO_COMPLY error
+ response SHOULD be used. Retrying would consume valuable resources
+ during an occurrence of overload.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 28]
+
+RFC 7683 DOIC October 2015
+
+
+ For instance, if the request arrived at the reporting node without
+ a Destination-Host AVP, then the reporting node might determine
+ that there is an alternative Diameter node that could successfully
+ process the request and that retrying the transaction would not
+ negatively impact the reporting node. DIAMETER_TOO_BUSY would be
+ sent in this case.
+
+ If the request arrived at the reporting node with a Destination-
+ Host AVP populated with its own Diameter identity, then the
+ reporting node can assume that retrying the request would result
+ in it coming to the same reporting node.
+ DIAMETER_UNABLE_TO_COMPLY would be sent in this case.
+
+ A second example is when an agent that supports the DOIC solution
+ is performing the role of a reacting node for a non-supporting
+ client. Requests that are rejected as a result of DOIC throttling
+ by the agent in this scenario would generally be rejected with a
+ DIAMETER_UNABLE_TO_COMPLY response code.
+
+9. IANA Considerations
+
+9.1. AVP Codes
+
+ New AVPs defined by this specification are listed in Section 7. All
+ AVP codes are allocated from the "AVP Codes" sub-registry under the
+ "Authentication, Authorization, and Accounting (AAA) Parameters"
+ registry.
+
+9.2. New Registries
+
+ Two new registries have been created in the "AVP Specific Values"
+ sub-registry under the "Authentication, Authorization, and Accounting
+ (AAA) Parameters" registry.
+
+ A new "OC-Feature-Vector AVP Values (code 622)" registry has been
+ created. This registry contains the following:
+
+ Feature Vector Value Name
+
+ Feature Vector Value
+
+ Specification defining the new value
+
+ See Section 7.2 for the initial Feature Vector Value in the registry.
+ This specification defines the value. New values can be added to the
+ registry using the Specification Required policy [RFC5226].
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 29]
+
+RFC 7683 DOIC October 2015
+
+
+ A new "OC-Report-Type AVP Values (code 626)" registry has been
+ created. This registry contains the following:
+
+ Report Type Value Name
+
+ Report Type Value
+
+ Specification defining the new value
+
+ See Section 7.6 for the initial assignment in the registry. New
+ types can be added using the Specification Required policy [RFC5226].
+
+10. Security Considerations
+
+ DOIC gives Diameter nodes the ability to request that downstream
+ nodes send fewer Diameter requests. Nodes do this by exchanging
+ overload reports that directly effect this reduction. This exchange
+ is potentially subject to multiple methods of attack and has the
+ potential to be used as a denial-of-service (DoS) attack vector. For
+ instance, a series of injected realm OLRs with a requested reduction
+ percentage of 100% could be used to completely eliminate any traffic
+ from being sent to that realm.
+
+ Overload reports may contain information about the topology and
+ current status of a Diameter network. This information is
+ potentially sensitive. Network operators may wish to control
+ disclosure of overload reports to unauthorized parties to avoid their
+ use for competitive intelligence or to target attacks.
+
+ Diameter does not include features to provide end-to-end
+ authentication, integrity protection, or confidentiality. This may
+ cause complications when sending overload reports between non-
+ adjacent nodes.
+
+10.1. Potential Threat Modes
+
+ The Diameter protocol involves transactions in the form of requests
+ and answers exchanged between clients and servers. These clients and
+ servers may be peers, that is, they may share a direct transport
+ (e.g., TCP or SCTP) connection, or the messages may traverse one or
+ more intermediaries, known as Diameter Agents. Diameter nodes use
+ TLS, DTLS, or IPsec to authenticate peers and to provide
+ confidentiality and integrity protection of traffic between peers.
+ Nodes can make authorization decisions based on the peer identities
+ authenticated at the transport layer.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 30]
+
+RFC 7683 DOIC October 2015
+
+
+ When agents are involved, this presents an effectively transitive
+ trust model. That is, a Diameter client or server can authorize an
+ agent for certain actions, but it must trust that agent to make
+ appropriate authorization decisions about its peers, and so on.
+ Since confidentiality and integrity protection occur at the transport
+ layer, agents can read, and perhaps modify, any part of a Diameter
+ message, including an overload report.
+
+ There are several ways an attacker might attempt to exploit the
+ overload control mechanism. An unauthorized third party might inject
+ an overload report into the network. If this third party is upstream
+ of an agent, and that agent fails to apply proper authorization
+ policies, downstream nodes may mistakenly trust the report. This
+ attack is at least partially mitigated by the assumption that nodes
+ include overload reports in Diameter answers but not in requests.
+ This requires an attacker to have knowledge of the original request
+ in order to construct an answer. Such an answer would also need to
+ arrive at a Diameter node via a protected transport connection.
+ Therefore, implementations MUST validate that an answer containing an
+ overload report is a properly constructed response to a pending
+ request prior to acting on the overload report, and that the answer
+ was received via an appropriate transport connection.
+
+ A similar attack involves a compromised but otherwise authorized node
+ that sends an inappropriate overload report. For example, a server
+ for the realm "example.com" might send an overload report indicating
+ that a competitor's realm "example.net" is overloaded. If other
+ nodes act on the report, they may falsely believe that "example.net"
+ is overloaded, effectively reducing that realm's capacity.
+ Therefore, it's critical that nodes validate that an overload report
+ received from a peer actually falls within that peer's responsibility
+ before acting on the report or forwarding the report to other peers.
+ For example, an overload report from a peer that applies to a realm
+ not handled by that peer is suspect. This may require out-of-band,
+ non-Diameter agreements and/or mechanisms.
+
+ This attack is partially mitigated by the fact that the
+ application, as well as host and realm, for a given OLR is
+ determined implicitly by respective AVPs in the enclosing answer.
+ If a reporting node modifies any of those AVPs, the enclosing
+ transaction will also be affected.
+
+10.2. Denial-of-Service Attacks
+
+ Diameter overload reports, especially realm reports, can cause a node
+ to cease sending some or all Diameter requests for an extended
+ period. This makes them a tempting vector for DoS attacks.
+ Furthermore, since Diameter is almost always used in support of other
+
+
+
+Korhonen, et al. Standards Track [Page 31]
+
+RFC 7683 DOIC October 2015
+
+
+ protocols, a DoS attack on Diameter is likely to impact those
+ protocols as well. In the worst case, where the Diameter application
+ is being used for access control into an IP network, a coordinated
+ DoS attack could result in the blockage of all traffic into that
+ network. Therefore, Diameter nodes MUST NOT honor or forward OLRs
+ received from peers that are not trusted to send them.
+
+ An attacker might use the information in an OLR to assist in DoS
+ attacks. For example, an attacker could use information about
+ current overload conditions to time an attack for maximum effect, or
+ use subsequent overload reports as a feedback mechanism to learn the
+ results of a previous or ongoing attack. Operators need the ability
+ to ensure that OLRs are not leaked to untrusted parties.
+
+10.3. Noncompliant Nodes
+
+ In the absence of an overload control mechanism, Diameter nodes need
+ to implement strategies to protect themselves from floods of
+ requests, and to make sure that a disproportionate load from one
+ source does not prevent other sources from receiving service. For
+ example, a Diameter server might throttle a certain percentage of
+ requests from sources that exceed certain limits. Overload control
+ can be thought of as an optimization for such strategies, where
+ downstream nodes never send the excess requests in the first place.
+ However, the presence of an overload control mechanism does not
+ remove the need for these other protection strategies.
+
+ When a Diameter node sends an overload report, it cannot assume that
+ all nodes will comply, even if they indicate support for DOIC. A
+ noncompliant node might continue to send requests with no reduction
+ in load. Such noncompliance could be done accidentally or
+ maliciously to gain an unfair advantage over compliant nodes.
+ Requirement 28 in [RFC7068] indicates that the overload control
+ solution cannot assume that all Diameter nodes in a network are
+ trusted. It also requires that malicious nodes not be allowed to
+ take advantage of the overload control mechanism to get more than
+ their fair share of service.
+
+10.4. End-to-End Security Issues
+
+ The lack of end-to-end integrity features makes it difficult to
+ establish trust in overload reports received from non-adjacent nodes.
+ Any agents in the message path may insert or modify overload reports.
+ Nodes must trust that their adjacent peers perform proper checks on
+ overload reports from their peers, and so on, creating a transitive-
+ trust requirement extending for potentially long chains of nodes.
+ Network operators must determine if this transitive trust requirement
+ is acceptable for their deployments. Nodes supporting Diameter
+
+
+
+Korhonen, et al. Standards Track [Page 32]
+
+RFC 7683 DOIC October 2015
+
+
+ overload control MUST give operators the ability to select which
+ peers are trusted to deliver overload reports and whether they are
+ trusted to forward overload reports from non-adjacent nodes. DOIC
+ nodes MUST strip DOIC AVPs from messages received from peers that are
+ not trusted for DOIC purposes.
+
+ The lack of end-to-end confidentiality protection means that any
+ Diameter Agent in the path of an overload report can view the
+ contents of that report. In addition to the requirement to select
+ which peers are trusted to send overload reports, operators MUST be
+ able to select which peers are authorized to receive reports. A node
+ MUST NOT send an overload report to a peer not authorized to receive
+ it. Furthermore, an agent MUST remove any overload reports that
+ might have been inserted by other nodes before forwarding a Diameter
+ message to a peer that is not authorized to receive overload reports.
+
+ A DOIC node cannot always automatically detect that a peer also
+ supports DOIC. For example, a node might have a peer that is a
+ non-supporting agent. If nodes on the other side of that agent
+ send OC-Supported-Features AVPs, the agent is likely to forward
+ them as unknown AVPs. Messages received across the non-supporting
+ agent may be indistinguishable from messages received across a
+ DOIC supporting agent, giving the false impression that the non-
+ supporting agent actually supports DOIC. This complicates the
+ transitive-trust nature of DOIC. Operators need to be careful to
+ avoid situations where a non-supporting agent is mistakenly
+ trusted to enforce DOIC-related authorization policies.
+
+ It is expected that work on end-to-end Diameter security might make
+ it easier to establish trust in non-adjacent nodes for overload
+ control purposes. Readers should be reminded, however, that the
+ overload control mechanism allows Diameter Agents to modify AVPs in,
+ or insert additional AVPs into, existing messages that are originated
+ by other nodes. If end-to-end security is enabled, there is a risk
+ that such modification could violate integrity protection. The
+ details of using any future Diameter end-to-end security mechanism
+ with overload control will require careful consideration, and are
+ beyond the scope of this document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 33]
+
+RFC 7683 DOIC October 2015
+
+
+11. References
+
+11.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119,
+ DOI 10.17487/RFC2119, March 1997,
+ <http://www.rfc-editor.org/info/rfc2119>.
+
+ [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
+ IANA Considerations Section in RFCs", BCP 26, RFC 5226,
+ DOI 10.17487/RFC5226, May 2008,
+ <http://www.rfc-editor.org/info/rfc5226>.
+
+ [RFC6733] Fajardo, V., Ed., Arkko, J., Loughney, J., and G. Zorn,
+ Ed., "Diameter Base Protocol", RFC 6733,
+ DOI 10.17487/RFC6733, October 2012,
+ <http://www.rfc-editor.org/info/rfc6733>.
+
+11.2. Informative References
+
+ [Cx] 3GPP, "Cx and Dx interfaces based on the Diameter
+ protocol; Protocol details", 3GPP TS 29.229 12.7.0,
+ September 2015.
+
+ [PCC] 3GPP, "Policy and charging control architecture", 3GPP
+ TS 23.203 12.10.0, September 2015.
+
+ [RFC4006] Hakala, H., Mattila, L., Koskinen, J-P., Stura, M., and J.
+ Loughney, "Diameter Credit-Control Application", RFC 4006,
+ DOI 10.17487/RFC4006, August 2005,
+ <http://www.rfc-editor.org/info/rfc4006>.
+
+ [RFC7068] McMurry, E. and B. Campbell, "Diameter Overload Control
+ Requirements", RFC 7068, DOI 10.17487/RFC7068, November
+ 2013, <http://www.rfc-editor.org/info/rfc7068>.
+
+ [S13] 3GPP, "Evolved Packet System (EPS); Mobility Management
+ Entity (MME) and Serving GPRS Support Node (SGSN) related
+ interfaces based on Diameter protocol", 3GPP TS 29.272
+ 12.8.0, September 2015.
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 34]
+
+RFC 7683 DOIC October 2015
+
+
+Appendix A. Issues Left for Future Specifications
+
+ The base solution for overload control does not cover all possible
+ use cases. A number of solution aspects were intentionally left for
+ future specification and protocol work. The following subsections
+ define some of the potential extensions to the DOIC solution.
+
+A.1. Additional Traffic Abatement Algorithms
+
+ This specification describes only means for a simple loss-based
+ algorithm. Future algorithms can be added using the designed
+ solution extension mechanism. The new algorithms need to be
+ registered with IANA. See Sections 7.2 and 9 for the required IANA
+ steps.
+
+A.2. Agent Overload
+
+ This specification focuses on Diameter endpoint (server or client)
+ overload. A separate extension will be required to outline the
+ handling of the case of agent overload.
+
+A.3. New Error Diagnostic AVP
+
+ This specification indicates the use of existing error messages when
+ nodes reject requests due to overload. There is an expectation that
+ additional error codes or AVPs will be defined in a separate
+ specification to indicate that overload was the reason for the
+ rejection of the message.
+
+Appendix B. Deployment Considerations
+
+ Non-supporting Agents
+
+ Due to the way that realm-routed requests are handled in Diameter
+ networks with the server selection for the request done by an
+ agent, network operators should enable DOIC at agents that perform
+ server selection first.
+
+ Topology-Hiding Interactions
+
+ There exist proxies that implement what is referred to as Topology
+ Hiding. This can include cases where the agent modifies the
+ Origin-Host in answer messages. The behavior of the DOIC solution
+ is not well understood when this happens. As such, the DOIC
+ solution does not address this scenario.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 35]
+
+RFC 7683 DOIC October 2015
+
+
+ Inter-Realm/Administrative Domain Considerations
+
+ There are likely to be special considerations for handling DOIC
+ signaling across administrative boundaries. This includes
+ considerations for whether or not information included in the DOIC
+ signaling should be sent across those boundaries. In addition,
+ consideration should be taken as to whether or not a reacting node
+ in one realm can be trusted to implement the requested overload
+ abatement handling for overload reports received from a separately
+ administered realm.
+
+Appendix C. Considerations for Applications Integrating the DOIC
+ Solution
+
+ This section outlines considerations to be taken into account when
+ integrating the DOIC solution into Diameter applications.
+
+C.1. Application Classification
+
+ The following is a classification of Diameter applications and
+ request types. This discussion is meant to document factors that
+ play into decisions made by the Diameter entity responsible for
+ handling overload reports.
+
+ Section 8.1 of [RFC6733] defines two state machines that imply two
+ types of applications, session-less and session-based applications.
+ The primary difference between these types of applications is the
+ lifetime of Session-Ids.
+
+ For session-based applications, the Session-Id is used to tie
+ multiple requests into a single session.
+
+ The Credit-Control application defined in [RFC4006] is an example of
+ a Diameter session-based application.
+
+ In session-less applications, the lifetime of the Session-Id is a
+ single Diameter transaction, i.e., the session is implicitly
+ terminated after a single Diameter transaction and a new Session-Id
+ is generated for each Diameter request.
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 36]
+
+RFC 7683 DOIC October 2015
+
+
+ For the purposes of this discussion, session-less applications are
+ further divided into two types of applications:
+
+ Stateless Applications:
+
+ Requests within a stateless application have no relationship to
+ each other. The 3GPP-defined S13 application is an example of a
+ stateless application [S13], where only a Diameter command is
+ defined between a client and a server and no state is maintained
+ between two consecutive transactions.
+
+ Pseudo-Session Applications:
+
+ Applications that do not rely on the Session-Id AVP for
+ correlation of application messages related to the same session
+ but use other session-related information in the Diameter requests
+ for this purpose. The 3GPP-defined Cx application [Cx] is an
+ example of a pseudo-session application.
+
+ The handling of overload reports must take the type of application
+ into consideration, as discussed in Appendix C.2.
+
+C.2. Implications of Application Type Overload
+
+ This section discusses considerations for mitigating overload
+ reported by a Diameter entity. This discussion focuses on the type
+ of application. Appendix C.3 discusses considerations for handling
+ various request types when the target server is known to be in an
+ overloaded state.
+
+ These discussions assume that the strategy for mitigating the
+ reported overload is to reduce the overall workload sent to the
+ overloaded entity. The concept of applying overload treatment to
+ requests targeted for an overloaded Diameter entity is inherent to
+ this discussion. The method used to reduce offered load is not
+ specified here, but it could include routing requests to another
+ Diameter entity known to be able to handle them, or it could mean
+ rejecting certain requests. For a Diameter Agent, rejecting requests
+ will usually mean generating appropriate Diameter error responses.
+ For a Diameter client, rejecting requests will depend upon the
+ application. For example, it could mean giving an indication to the
+ entity requesting the Diameter service that the network is busy and
+ to try again later.
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 37]
+
+RFC 7683 DOIC October 2015
+
+
+ Stateless Applications:
+
+ By definition, there is no relationship between individual
+ requests in a stateless application. As a result, when a request
+ is sent or relayed to an overloaded Diameter entity -- either a
+ Diameter Server or a Diameter Agent -- the sending or relaying
+ entity can choose to apply the overload treatment to any request
+ targeted for the overloaded entity.
+
+ Pseudo-session Applications:
+
+ For pseudo-session applications, there is an implied ordering of
+ requests. As a result, decisions about which requests towards an
+ overloaded entity to reject could take the command code of the
+ request into consideration. This generally means that
+ transactions later in the sequence of transactions should be given
+ more favorable treatment than messages earlier in the sequence.
+ This is because more work has already been done by the Diameter
+ network for those transactions that occur later in the sequence.
+ Rejecting them could result in increasing the load on the network
+ as the transactions earlier in the sequence might also need to be
+ repeated.
+
+ Session-Based Applications:
+
+ Overload handling for session-based applications must take into
+ consideration the work load associated with setting up and
+ maintaining a session. As such, the entity sending requests
+ towards an overloaded Diameter entity for a session-based
+ application might tend to reject new session requests prior to
+ rejecting intra-session requests. In addition, session-ending
+ requests might be given a lower probability of being rejected, as
+ rejecting session-ending requests could result in session status
+ being out of sync between the Diameter clients and servers.
+ Application designers that would decide to reject mid-session
+ requests will need to consider whether the rejection invalidates
+ the session and any resulting session cleanup procedures.
+
+C.3. Request Transaction Classification
+
+ Independent Request:
+
+ An independent request is not correlated to any other requests,
+ and, as such, the lifetime of the Session-Id is constrained to an
+ individual transaction.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 38]
+
+RFC 7683 DOIC October 2015
+
+
+ Session-Initiating Request:
+
+ A session-initiating request is the initial message that
+ establishes a Diameter session. The ACR message defined in
+ [RFC6733] is an example of a session-initiating request.
+
+ Correlated Session-Initiating Request:
+
+ There are cases when multiple session-initiated requests must be
+ correlated and managed by the same Diameter server. It is notably
+ the case in the 3GPP Policy and Charging Control (PCC)
+ architecture [PCC], where multiple apparently independent Diameter
+ application sessions are actually correlated and must be handled
+ by the same Diameter server.
+
+ Intra-session Request:
+
+ An intra-session request is a request that uses the same Session-
+ Id as the one used in a previous request. An intra-session
+ request generally needs to be delivered to the server that handled
+ the session-creating request for the session. The STR message
+ defined in [RFC6733] is an example of an intra-session request.
+
+ Pseudo-session Requests:
+
+ Pseudo-session requests are independent requests and do not use
+ the same Session-Id but are correlated by other session-related
+ information contained in the request. There exist Diameter
+ applications that define an expected ordering of transactions.
+ This sequencing of independent transactions results in a pseudo-
+ session. The AIR, MAR, and SAR requests in the 3GPP-defined Cx
+ [Cx] application are examples of pseudo-session requests.
+
+C.4. Request Type Overload Implications
+
+ The request classes identified in Appendix C.3 have implications on
+ decisions about which requests should be throttled first. The
+ following list of request treatments regarding throttling is provided
+ as guidelines for application designers when implementing the
+ Diameter overload control mechanism described in this document. The
+ exact behavior regarding throttling is a matter of local policy,
+ unless specifically defined for the application.
+
+ Independent Requests:
+
+ Independent requests can generally be given equal treatment when
+ making throttling decisions, unless otherwise indicated by
+ application requirements or local policy.
+
+
+
+Korhonen, et al. Standards Track [Page 39]
+
+RFC 7683 DOIC October 2015
+
+
+ Session-Initiating Requests:
+
+ Session-initiating requests often represent more work than
+ independent or intra-session requests. Moreover, session-
+ initiating requests are typically followed by other session-
+ related requests. Since the main objective of overload control is
+ to reduce the total number of requests sent to the overloaded
+ entity, throttling decisions might favor allowing intra-session
+ requests over session-initiating requests. In the absence of
+ local policies or application-specific requirements to the
+ contrary, individual session-initiating requests can be given
+ equal treatment when making throttling decisions.
+
+ Correlated Session-Initiating Requests:
+
+ A request that results in a new binding; where the binding is used
+ for routing of subsequent session-initiating requests to the same
+ server, it represents more work load than other requests. As
+ such, these requests might be throttled more frequently than other
+ request types.
+
+ Pseudo-session Requests:
+
+ Throttling decisions for pseudo-session requests can take into
+ consideration where individual requests fit into the overall
+ sequence of requests within the pseudo-session. Requests that are
+ earlier in the sequence might be throttled more aggressively than
+ requests that occur later in the sequence.
+
+ Intra-session Requests:
+
+ There are two types of intra-sessions requests, requests that
+ terminate a session and the remainder of intra-session requests.
+ Implementers and operators may choose to throttle session-
+ terminating requests less aggressively in order to gracefully
+ terminate sessions, allow cleanup of the related resources (e.g.,
+ session state), and avoid the need for additional intra-session
+ requests. Favoring session termination requests may reduce the
+ session management impact on the overloaded entity. The default
+ handling of other intra-session requests might be to treat them
+ equally when making throttling decisions. There might also be
+ application-level considerations whether some request types are
+ favored over others.
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 40]
+
+RFC 7683 DOIC October 2015
+
+
+Contributors
+
+ The following people contributed substantial ideas, feedback, and
+ discussion to this document:
+
+ o Eric McMurry
+
+ o Hannes Tschofenig
+
+ o Ulrich Wiehe
+
+ o Jean-Jacques Trottin
+
+ o Maria Cruz Bartolome
+
+ o Martin Dolly
+
+ o Nirav Salot
+
+ o Susan Shishufeng
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 41]
+
+RFC 7683 DOIC October 2015
+
+
+Authors' Addresses
+
+ Jouni Korhonen (editor)
+ Broadcom Corporation
+ 3151 Zanker Road
+ San Jose, CA 95134
+ United States
+
+
+
+ Steve Donovan (editor)
+ Oracle
+ 7460 Warren Parkway
+ Frisco, Texas 75034
+ United States
+
+
+
+ Ben Campbell
+ Oracle
+ 7460 Warren Parkway
+ Frisco, Texas 75034
+ United States
+
+
+
+ Lionel Morand
+ Orange Labs
+ 38/40 rue du General Leclerc
+ Issy-Les-Moulineaux Cedex 9 92794
+ France
+
+ Phone: +33145296257
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 42]
+
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 6bf748a727..3af856f63e 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2016. All Rights Reserved.
+# Copyright Ericsson AB 2010-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -274,9 +274,7 @@ gen/diameter_gen_base_accounting.erl gen/diameter_gen_base_accounting.hrl: \
gen/diameter_gen_acct_rfc6733.erl gen/diameter_gen_acct_rfc6733.hrl: \
$(EBIN)/diameter_gen_base_rfc6733.$(EMULATOR)
-gen/diameter_gen_relay.erl gen/diameter_gen_relay.hrl \
-gen/diameter_gen_base_rfc3588.erl gen/diameter_gen_base_rfc3588.hrl \
-gen/diameter_gen_base_rfc6733.erl gen/diameter_gen_base_rfc6733.hrl: \
+$(DICT_ERLS) $(DICT_HRLS): \
$(COMPILER_MODULES:%=$(EBIN)/%.$(EMULATOR))
$(DICT_MODULES:gen/%=$(EBIN)/%.$(EMULATOR)): \
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index 3b41feac0d..b90b794611 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -340,7 +340,7 @@ call(SvcName, App, Message) ->
:: record
| list
| map
- | false
+ | none
| record_from_map.
-type strict_arities()
@@ -356,6 +356,7 @@ call(SvcName, App, Message) ->
| {capx_timeout, 'Unsigned32'()}
| {strict_capx, boolean()}
| {strict_mbit, boolean()}
+ | {avp_dictionaries, [module()]}
| {disconnect_cb, eval()}
| {dpr_timeout, 'Unsigned32'()}
| {dpa_timeout, 'Unsigned32'()}
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index 63e39b12d1..2dd2c906a2 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -324,7 +324,7 @@ decode_avps(MsgName, Mod, AppMod, Opts, #diameter_packet{bin = Bin} = Pkt) ->
{_, Avps} = split_binary(Bin, 20),
{Rec, As, Errors} = Mod:decode_avps(MsgName,
Avps,
- Opts#{dictionary => AppMod,
+ Opts#{app_dictionary => AppMod,
failed_avp => false}),
?LOGC([] /= Errors, decode_errors, Pkt#diameter_packet.header),
Pkt#diameter_packet{msg = reformat(MsgName, Rec, Opts),
@@ -614,8 +614,8 @@ pack_avp(#diameter_avp{data = {T, {Type, Value}}}, Opts) ->
pack_avp(#diameter_avp{data = {T, Data}}, _) ->
pack_data(T, Data);
-pack_avp(#diameter_avp{data = {Dict, Name, Data}}, Opts) ->
- pack_data(Dict:avp_header(Name), Dict:avp(encode, Data, Name, Opts));
+pack_avp(#diameter_avp{data = {Dict, Name, Value}}, Opts) ->
+ pack_data(Dict:avp_header(Name), Dict:avp(encode, Value, Name, Opts));
%% ... with a truncated header ...
pack_avp(#diameter_avp{code = undefined, data = B}, _)
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 4721be1ca0..90a9282349 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -588,8 +588,7 @@ opt(service, {K, false})
K == use_shared_peers;
K == monitor;
K == restrict_connections;
- K == strict_arities;
- K == decode_format ->
+ K == strict_arities ->
true;
opt(service, {K, true})
@@ -602,6 +601,7 @@ opt(service, {decode_format, T})
when T == record;
T == list;
T == map;
+ T == none;
T == record_from_map ->
true;
@@ -665,9 +665,8 @@ opt(transport, {applications, As}) ->
is_list(As);
opt(transport, {capabilities, Os}) ->
- is_list(Os) andalso case encode_CER(Os) of
- ok -> true;
- No -> {error, No}
+ is_list(Os) andalso try ok = encode_CER(Os), true
+ catch ?FAILURE(No) -> {error, No}
end;
opt(_, {K, Tmo})
@@ -683,6 +682,9 @@ opt(_, {K, B})
K == strict_mbit ->
is_boolean(B);
+opt(_, {avp_dictionaries, Mods}) ->
+ is_list(Mods) andalso lists:all(fun erlang:is_atom/1, Mods);
+
opt(_, {length_errors, T}) ->
lists:member(T, [exit, handle, discard]);
diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl
index 7a1a46ec52..6add06ea38 100644
--- a/lib/diameter/src/base/diameter_gen.erl
+++ b/lib/diameter/src/base/diameter_gen.erl
@@ -45,7 +45,7 @@
-define(THROW(T), throw({?MODULE, T})).
-type parent_name() :: atom(). %% parent = Message or AVP
--type parent_record() :: tuple(). %%
+-type parent_record() :: tuple() | avp_values() | map().
-type avp_name() :: atom().
-type avp_record() :: tuple().
-type avp_values() :: [{avp_name(), term()}].
@@ -61,9 +61,7 @@
%% # encode_avps/3
%% ---------------------------------------------------------------------------
--spec encode_avps(parent_name(),
- parent_record() | avp_values() | map(),
- map())
+-spec encode_avps(parent_name(), parent_record(), map())
-> iolist()
| no_return().
@@ -102,73 +100,73 @@ encode(Name, Vals, Opts, Strict, Mod)
encode(Name, Map, Opts, Strict, Mod)
when is_map(Map) ->
- [enc(Name, F, A, V, Opts, Strict, Mod) || {F,A} <- Mod:avp_arity(Name),
- V <- [mget(F, Map, undefined)]];
+ [enc(F, A, V, Opts, Strict, Mod) || {F,A} <- Mod:avp_arity(Name),
+ V <- [mget(F, Map, undefined)]];
encode(Name, Rec, Opts, Strict, Mod) ->
[encode(Name, F, V, Opts, Strict, Mod) || {F,V} <- Mod:'#get-'(Rec)].
%% encode/6
-encode(Name, AvpName, Values, Opts, Strict, Mod)
+encode(_, AvpName, Values, Opts, Strict, Mod)
when Strict /= encode ->
- enc(Name, AvpName, ?ANY, Values, Opts, Strict, Mod);
+ enc(AvpName, ?ANY, Values, Opts, Strict, Mod);
encode(Name, AvpName, Values, Opts, Strict, Mod) ->
Arity = Mod:avp_arity(Name, AvpName),
- enc(Name, AvpName, Arity, Values, Opts, Strict, Mod).
+ enc(AvpName, Arity, Values, Opts, Strict, Mod).
-%% enc/7
+%% enc/6
-enc(Name, AvpName, Arity, Values, Opts, Strict, Mod)
+enc(AvpName, Arity, Values, Opts, Strict, Mod)
when Strict /= encode, Arity /= ?ANY ->
- enc(Name, AvpName, ?ANY, Values, Opts, Strict, Mod);
+ enc(AvpName, ?ANY, Values, Opts, Strict, Mod);
-enc(_, AvpName, 1, undefined, _, _, _) ->
+enc(AvpName, 1, undefined, _, _, _) ->
?THROW([mandatory_avp_missing, AvpName]);
-enc(Name, AvpName, 1, Value, Opts, _, Mod) ->
+enc(AvpName, 1, Value, Opts, _, Mod) ->
H = avp_header(AvpName, Mod),
- enc1(Name, AvpName, H, Value, Opts, Mod);
+ enc(AvpName, H, Value, Opts, Mod);
-enc(_, _, {0,_}, [], _, _, _) ->
+enc(_, {0,_}, [], _, _, _) ->
[];
-enc(_, _, _, undefined, _, _, _) ->
+enc(_, _, undefined, _, _, _) ->
[];
%% Be forgiving when a list of values is expected. If the value itself
%% is a list then the user has to wrap it to avoid each member from
%% being interpreted as an individual AVP value.
-enc(Name, AvpName, Arity, V, Opts, Strict, Mod)
+enc(AvpName, Arity, V, Opts, Strict, Mod)
when not is_list(V) ->
- enc(Name, AvpName, Arity, [V], Opts, Strict, Mod);
+ enc(AvpName, Arity, [V], Opts, Strict, Mod);
-enc(Name, AvpName, {Min, Max}, Values, Opts, Strict, Mod) ->
+enc(AvpName, {Min, Max}, Values, Opts, Strict, Mod) ->
H = avp_header(AvpName, Mod),
- enc(Name, AvpName, H, Min, 0, Max, Values, Opts, Strict, Mod).
+ enc(AvpName, H, Min, 0, Max, Values, Opts, Strict, Mod).
-%% enc/10
+%% enc/9
-enc(Name, AvpName, H, Min, N, Max, Vs, Opts, Strict, Mod)
+enc(AvpName, H, Min, N, Max, Vs, Opts, Strict, Mod)
when Strict /= encode;
Max == '*', Min =< N ->
- [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs];
+ [enc(AvpName, H, V, Opts, Mod) || V <- Vs];
-enc(_, AvpName, _, Min, N, _, [], _, _, _)
+enc(AvpName, _, Min, N, _, [], _, _, _)
when N < Min ->
?THROW([repeated_avp_insufficient_arity, AvpName, Min, N]);
-enc(_, _, _, _, _, _, [], _, _, _) ->
+enc(_, _, _, _, _, [], _, _, _) ->
[];
-enc(_, AvpName, _, _, N, Max, _, _, _, _)
+enc(AvpName, _, _, N, Max, _, _, _, _)
when Max =< N ->
?THROW([repeated_avp_excessive_arity, AvpName, Max]);
-enc(Name, AvpName, H, Min, N, Max, [V|Vs], Opts, Strict, Mod) ->
- [enc1(Name, AvpName, H, V, Opts, Mod)
- | enc(Name, AvpName, H, Min, N+1, Max, Vs, Opts, Strict, Mod)].
+enc(AvpName, H, Min, N, Max, [V|Vs], Opts, Strict, Mod) ->
+ [enc(AvpName, H, V, Opts, Mod)
+ | enc(AvpName, H, Min, N+1, Max, Vs, Opts, Strict, Mod)].
%% avp_header/2
@@ -178,12 +176,12 @@ avp_header('AVP', _) ->
avp_header(AvpName, Mod) ->
{_,_,_} = Mod:avp_header(AvpName).
-%% enc1/6
+%% enc/5
-enc1(Name, 'AVP', false, Value, Opts, Mod) ->
- enc_AVP(Name, Value, Opts, Mod);
+enc('AVP', false, Value, Opts, Mod) ->
+ enc_AVP(Value, Opts, Mod);
-enc1(_, AvpName, Hdr, Value, Opts, Mod) ->
+enc(AvpName, Hdr, Value, Opts, Mod) ->
enc1(AvpName, Hdr, Value, Opts, Mod).
%% enc1/5
@@ -191,54 +189,72 @@ enc1(_, AvpName, Hdr, Value, Opts, Mod) ->
enc1(AvpName, {_,_,_} = Hdr, Value, Opts, Mod) ->
diameter_codec:pack_data(Hdr, Mod:avp(encode, Value, AvpName, Opts)).
-%% enc_AVP/4
+%% enc1/6
+
+enc1(AvpName, {_,_,_} = Hdr, Value, Opts, Mod, Dict) ->
+ diameter_codec:pack_data(Hdr, avp(encode, Value, AvpName, Opts, Mod, Dict)).
+
+%% enc_AVP/3
%% No value: assume AVP data is already encoded. The normal case will
%% be when this is passed back from #diameter_packet.errors as a
%% consequence of a failed decode. Any AVP can be encoded this way
%% however, which side-steps any arity checks for known AVP's and
%% could potentially encode something unfortunate.
-enc_AVP(_, #diameter_avp{value = undefined} = A, Opts, _) ->
+enc_AVP(#diameter_avp{value = undefined} = A, Opts, _) ->
diameter_codec:pack_avp(A, Opts);
-%% Missing name for value encode.
-enc_AVP(_, #diameter_avp{name = N, value = V}, _, _)
- when N == undefined;
- N == 'AVP' ->
- ?THROW([value_with_nameless_avp, N, V]);
+%% Encode a name/value pair using an alternate dictionary if need be ...
+enc_AVP(#diameter_avp{name = AvpName, value = Value}, Opts, Mod) ->
+ enc_AVP(AvpName, Value, Opts, Mod);
+enc_AVP({AvpName, Value}, Opts, Mod) ->
+ enc_AVP(AvpName, Value, Opts, Mod);
-%% Or not. Ensure that 'AVP' is the appropriate field. Note that if we
-%% don't know this AVP at all then the encode will fail.
-enc_AVP(Name, #diameter_avp{name = AvpName, value = Data}, Opts, Mod) ->
- 0 == Mod:avp_arity(Name, AvpName)
- orelse ?THROW([known_avp_as_AVP, Name, AvpName, Data]),
- enc(AvpName, Data, Opts, Mod);
+%% ... or with a specified dictionary.
+enc_AVP({Dict, AvpName, Value}, Opts, Mod) ->
+ enc1(AvpName, Dict:avp_header(AvpName), Value, Opts, Mod, Dict).
-%% The backdoor ...
-enc_AVP(_, {AvpName, Value}, Opts, Mod) ->
- enc(AvpName, Value, Opts, Mod);
+%% Don't guard against anything being sent as a generic 'AVP', which
+%% allows arity restrictions to be abused.
-%% ... and the side door.
-enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) ->
- diameter_codec:pack_avp(#diameter_avp{data = T}, Opts).
+%% enc_AVP/4
-%% enc/4
+enc_AVP(AvpName, Value, Opts, Mod) ->
+ try Mod:avp_header(AvpName) of
+ H ->
+ enc1(AvpName, H, Value, Opts, Mod)
+ catch
+ error: _ ->
+ Dicts = mget(avp_dictionaries, Opts, []),
+ enc_AVP(Dicts, AvpName, Value, Opts, Mod)
+ end.
+
+%% enc_AVP/5
+
+enc_AVP([Dict | Rest], AvpName, Value, Opts, Mod) ->
+ try Dict:avp_header(AvpName) of
+ H ->
+ enc1(AvpName, H, Value, Opts, Mod, Dict)
+ catch
+ error: _ ->
+ enc_AVP(Rest, AvpName, Value, Opts, Mod)
+ end;
-enc(AvpName, Value, Opts, Mod) ->
- enc1(AvpName, Mod:avp_header(AvpName), Value, Opts, Mod).
+enc_AVP([], AvpName, _, _, _) ->
+ ?THROW([no_dictionary, AvpName]).
%% ---------------------------------------------------------------------------
%% # decode_avps/3
%% ---------------------------------------------------------------------------
-spec decode_avps(parent_name(), binary(), map())
- -> {parent_record(), [avp()], Failed}
+ -> {parent_record() | parent_name(), [avp()], Failed}
when Failed :: [{5000..5999, #diameter_avp{}}].
decode_avps(Name, Bin, #{module := Mod, decode_format := Fmt} = Opts) ->
Strict = mget(strict_arities, Opts, decode),
[AM, Avps, Failed | Rec]
- = decode(Bin, Name, Mod, Fmt, Strict, Opts, #{}),
+ = decode(Bin, Name, Mod, Fmt, Strict, Opts, 0, #{}),
%% AM counts the number of top-level AVPs, which missing/5 then
%% uses when appending 5005 errors.
{reformat(Name, Rec, Strict, Mod, Fmt),
@@ -249,7 +265,7 @@ decode_avps(Name, Bin, #{module := Mod, decode_format := Fmt} = Opts) ->
%% encountered. Failed-AVP should typically contain the first
%% error encountered.
-%% decode/7
+%% decode/8
decode(<<Code:32, V:1, M:1, P:1, _:5, Len:24, I:V/unit:32, Rest/binary>>,
Name,
@@ -257,6 +273,7 @@ decode(<<Code:32, V:1, M:1, P:1, _:5, Len:24, I:V/unit:32, Rest/binary>>,
Fmt,
Strict,
Opts,
+ Idx,
AM) ->
decode(Rest,
Code,
@@ -270,21 +287,23 @@ decode(<<Code:32, V:1, M:1, P:1, _:5, Len:24, I:V/unit:32, Rest/binary>>,
Fmt,
Strict,
Opts,
+ Idx,
AM);
-decode(<<>>, Name, Mod, Fmt, Strict, _, AM) ->
+decode(<<>>, Name, Mod, Fmt, Strict, _, _, AM) ->
[AM, [], [] | newrec(Fmt, Mod, Name, Strict)];
-decode(Bin, Name, Mod, Fmt, Strict, _, AM) ->
- Avp = #diameter_avp{data = Bin},
+decode(Bin, Name, Mod, Fmt, Strict, _, Idx, AM) ->
+ Avp = #diameter_avp{data = Bin, index = Idx},
[AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)].
-%% decode/13
+%% decode/14
-decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0, AM0) ->
+decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0,
+ Idx, AM0) ->
case Bin of
<<Data:DataLen/binary, _:Pad/binary, T/binary>> ->
- {NameT, AvpName, Arity, {Idx, AM}}
+ {NameT, Field, Arity, {I, AM}}
= incr(Name, Code, Vid, M, Mod, Strict, Opts0, AM0),
Opts = setopts(NameT, Name, M, Opts0),
@@ -300,11 +319,11 @@ decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0, AM0) -
type = type(NameT),
index = Idx},
- Dec = decode(Data, Name, NameT, Mod, Opts, Avp), %% decode
- Acc = decode(T, Name, Mod, Fmt, Strict, Opts, AM), %% recurse
- acc(Acc, Dec, Name, AvpName, Arity, Strict, Mod, Opts);
+ Dec = dec(Data, Name, NameT, Mod, Fmt, Opts, Avp),
+ Acc = decode(T, Name, Mod, Fmt, Strict, Opts, Idx+1, AM),%% recurse
+ acc(Acc, Dec, I, Field, Arity, Strict, Mod, Opts);
_ ->
- {NameT, _AvpName, _Arity, {Idx, AM}}
+ {NameT, _Field, _Arity, {_, AM}}
= incr(Name, Code, Vid, M, Mod, Strict, Opts0, AM0),
Avp = #diameter_avp{code = Code,
@@ -323,9 +342,14 @@ decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0, AM0) -
incr(Name, Code, Vid, M, Mod, Strict, Opts, AM0) ->
NameT = Mod:avp_name(Code, Vid), %% {AvpName, Type} | 'AVP'
- AvpName = field(NameT),
- Arity = avp_arity(Name, AvpName, Mod, Opts, M),
- {NameT, AvpName, Arity, incr(AvpName, Arity, Strict, AM0)}.
+ Field = field(NameT), %% AvpName | 'AVP'
+ Arity = avp_arity(Name, Field, Mod, Opts, M),
+ if 0 == Arity, 'AVP' /= Field ->
+ A = pack_arity(Name, Field, Opts, Mod, M),
+ {NameT, 'AVP', A, incr('AVP', A, Strict, AM0)};
+ true ->
+ {NameT, Field, Arity, incr(Field, Arity, Strict, AM0)}
+ end.
%% Data is a truncated header if command_code = undefined, otherwise
%% payload bytes. The former is padded to the length of a header if
@@ -342,9 +366,8 @@ setopts({_, Type}, Name, M, Opts) ->
%% incr/4
-incr(F, A, SA, AM)
- when F == 'AVP';
- A == ?ANY;
+incr(_, A, SA, AM)
+ when A == ?ANY;
A == 0;
SA /= decode ->
{undefined, AM};
@@ -444,12 +467,16 @@ field({AvpName, _}) ->
field(_) ->
'AVP'.
-%% decode/6
+%% dec/7
-%% AVP not in dictionary.
-decode(_Data, _Name, 'AVP', _Mod, _Opts, Avp) ->
+%% AVP not in dictionary: try an alternate.
+
+dec(_, _, 'AVP', _Mod, none, _, Avp) -> %% none decode is no-op
Avp;
+dec(Data, Name, 'AVP', Mod, Fmt, Opts, Avp) ->
+ dec_AVP(dicts(Mod, Opts), Data, Name, Mod, Fmt, Opts, Avp);
+
%% 6733, 4.4:
%%
%% Receivers of a Grouped AVP that does not have the 'M' (mandatory)
@@ -497,40 +524,100 @@ decode(_Data, _Name, 'AVP', _Mod, _Opts, Avp) ->
%% defined the RFC's "unrecognized", which is slightly stronger than
%% "not defined".)
-decode(Data, Name, {AvpName, Type}, Mod, Opts, Avp) ->
- #{dictionary := AppMod, failed_avp := Failed}
+dec(Data, Name, {AvpName, Type}, Mod, Fmt, Opts, Avp) ->
+ #{app_dictionary := AppMod, failed_avp := Failed}
= Opts,
%% Reset the dictionary for best-effort decode of Failed-AVP.
- DecMod = if Failed -> AppMod;
- true -> Mod
- end,
-
- %% A Grouped AVP is represented as a #diameter_avp{} list with AVP
- %% as head and component AVPs as tail. On encode, data can be a
- %% list of component AVPs.
-
- try avp_decode(Data, AvpName, Opts, DecMod, Mod) of
- {Rec, As} when Type == 'Grouped' ->
- A = Avp#diameter_avp{value = Rec},
- [A | As];
- V when Type /= 'Grouped' ->
- Avp#diameter_avp{value = V}
+ Dict = if Failed -> AppMod;
+ true -> Mod
+ end,
+
+ dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, Avp).
+
+%% dicts/2
+
+dicts(Mod, #{app_dictionary := Mod, avp_dictionaries := Dicts}) ->
+ Dicts;
+
+dicts(_, #{app_dictionary := Dict, avp_dictionaries := Dicts}) ->
+ [Dict | Dicts];
+
+dicts(Mod, #{app_dictionary := Mod}) ->
+ [];
+
+dicts(_, #{app_dictionary := Dict}) ->
+ [Dict].
+
+%% dec/10
+
+dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, Avp) ->
+ try avp(decode, Data, AvpName, Opts, Mod, Dict) of
+ V ->
+ set(Type, Fmt, Avp, V)
catch
throw: {?MODULE, T} ->
- decode_error(Failed, T, Avp);
+ decode_error(Failed, Fmt, T, Avp);
error: Reason ->
decode_error(Failed, Reason, Name, Mod, Opts, Avp)
end.
-%% decode_error/3
+%% dec_AVP/7
+
+dec_AVP([], _, _, _, _, _, Avp) ->
+ Avp;
+
+dec_AVP(Dicts, Data, Name, Mod, Fmt, Opts, #diameter_avp{code = Code,
+ vendor_id = Vid}
+ = Avp) ->
+ dec_AVP(Dicts, Data, Name, Mod, Fmt, Opts, Code, Vid, Avp).
+
+%% dec_AVP/9
+%%
+%% Try to decode an AVP in the first alternate dictionary that defines
+%% it.
+
+dec_AVP([Dict | Rest], Data, Name, Mod, Fmt, Opts, Code, Vid, Avp) ->
+ case Dict:avp_name(Code, Vid) of
+ {AvpName, Type} ->
+ A = Avp#diameter_avp{name = AvpName,
+ type = Type},
+ #{failed_avp := Failed} = Opts,
+ dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, A);
+ _ ->
+ dec_AVP(Rest, Data, Name, Mod, Fmt, Opts, Code, Vid, Avp)
+ end;
+
+dec_AVP([], _, _, _, _, _, _, _, Avp) ->
+ Avp.
+
+%% set/4
+%%
+%% A Grouped AVP is represented as a #diameter_avp{} list with AVP
+%% as head and component AVPs as tail.
+
+set('Grouped', none, Avp, V) ->
+ {_Rec, As} = V,
+ [Avp | As];
+
+set('Grouped', _, Avp, V) ->
+ {Rec, As} = V,
+ [Avp#diameter_avp{value = Rec} | As];
+
+set(_, _, Avp, V) ->
+ Avp#diameter_avp{value = V}.
+
+%% decode_error/4
%%
%% Error when decoding a grouped AVP.
-decode_error(true, {Rec, _, _}, Avp) ->
+decode_error(true, none, _, Avp) ->
+ Avp;
+
+decode_error(true, _, {Rec, _, _}, Avp) ->
Avp#diameter_avp{value = Rec};
-decode_error(false, {_, ComponentAvps, [{RC,A} | _]}, Avp) ->
+decode_error(false, _, {_, ComponentAvps, [{RC,A} | _]}, Avp) ->
{RC, [Avp | ComponentAvps], Avp#diameter_avp{data = [A]}}.
%% decode_error/6
@@ -548,13 +635,13 @@ decode_error(false, Reason, Name, Mod, Opts, Avp) ->
{Reason, Name, Avp#diameter_avp.name, Mod, Stack}),
rc(Reason, Avp, Opts, Mod).
-%% avp_decode/5
+%% avp/6
-avp_decode(Data, AvpName, Opts, Mod, Mod) ->
- Mod:avp(decode, Data, AvpName, Opts);
+avp(T, Data, AvpName, Opts, Mod, Mod) ->
+ Mod:avp(T, Data, AvpName, Opts);
-avp_decode(Data, AvpName, Opts, Mod, _) ->
- Mod:avp(decode, Data, AvpName, Opts, Mod).
+avp(T, Data, AvpName, Opts, _, Mod) ->
+ Mod:avp(T, Data, AvpName, Opts#{module := Mod}).
%% set_strict/3
%%
@@ -579,8 +666,8 @@ set_failed(_, Opts) ->
%% acc/8
-acc([AM | Acc], As, Name, AvpName, Arity, Strict, Mod, Opts) ->
- [AM | acc1(Acc, As, Name, AvpName, Arity, Strict, Mod, Opts)].
+acc([AM | Acc], As, I, Field, Arity, Strict, Mod, Opts) ->
+ [AM | acc1(Acc, As, I, Field, Arity, Strict, Mod, Opts)].
%% acc1/8
@@ -595,12 +682,20 @@ acc1(Acc, {RC, As, Avp}, _, _, _, _, _, _) ->
[[As | Avps], [{RC, Avp} | Failed] | Rec];
%% Grouped AVP ...
-acc1([Avps | Acc], [Avp|_] = As, Name, AvpName, Arity, Strict, Mod, Opts) ->
- [[As|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts)];
+acc1([Avps | Acc], [Avp|_] = As, I, Field, Arity, Strict, Mod, Opts) ->
+ [[As|Avps] | acc2(Acc, Avp, I, Field, Arity, Strict, Mod, Opts)];
%% ... or not.
-acc1([Avps | Acc], Avp, Name, AvpName, Arity, Strict, Mod, Opts) ->
- [[Avp|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts)].
+acc1([Avps | Acc], Avp, I, Field, Arity, Strict, Mod, Opts) ->
+ [[Avp|Avps] | acc2(Acc, Avp, I, Field, Arity, Strict, Mod, Opts)].
+
+%% The component list of a Grouped AVP is discarded when packing into
+%% the record (or equivalent): the values in an 'AVP' field are
+%% diameter_avp records, not a list of records in the Grouped case,
+%% and the decode into the value field is best-effort. The reason is
+%% history more than logic: it would probably have made more sense to
+%% retain the same structure as in diameter_packet.avps, but an 'AVP'
+%% list has always been flat.
%% acc2/8
@@ -609,29 +704,23 @@ acc2(Acc, Avp, _, 'AVP', 0, _, _, _) ->
[Failed | Rec] = Acc,
[[{rc(Avp), Avp} | Failed] | Rec];
-%% No AVP of this name: try to pack as 'AVP'.
-acc2(Acc, Avp, Name, AvpName, 0, Strict, Mod, Opts) ->
- M = Avp#diameter_avp.is_mandatory,
- Arity = pack_arity(Name, AvpName, Opts, Mod, M),
- acc2(Acc, Avp, Name, 'AVP', Arity, Strict, Mod, Opts);
-
%% Relaxed arities.
-acc2(Acc, Avp, _, AvpName, Arity, Strict, Mod, _)
+acc2(Acc, Avp, _, Field, Arity, Strict, Mod, _)
when Strict /= decode ->
- pack(Arity, AvpName, Avp, Mod, Acc);
+ pack(Arity, Field, Avp, Mod, Acc);
%% No maximum arity.
-acc2(Acc, Avp, _, AvpName, {_,'*'} = Arity, _, Mod, _) ->
- pack(Arity, AvpName, Avp, Mod, Acc);
+acc2(Acc, Avp, _, Field, {_,'*'} = Arity, _, Mod, _) ->
+ pack(Arity, Field, Avp, Mod, Acc);
%% Or check.
-acc2(Acc, Avp, _, AvpName, Arity, _, Mod, _) ->
+acc2(Acc, Avp, I, Field, Arity, _, Mod, _) ->
Mx = max_arity(Arity),
- if Mx =< Avp#diameter_avp.index ->
+ if Mx =< I ->
[Failed | Rec] = Acc,
[[{5009, Avp} | Failed] | Rec];
true ->
- pack(Arity, AvpName, Avp, Mod, Acc)
+ pack(Arity, Field, Avp, Mod, Acc)
end.
%% 3588/6733:
@@ -723,8 +812,9 @@ pack(Arity, F, Avp, Mod, [Failed | Rec]) ->
%% set/5
-set(_, _, _, _, false = No) ->
- No;
+set(_, _, _, _, None)
+ when is_atom(None) ->
+ None;
set(1, F, Value, _, Map)
when is_map(Map) ->
@@ -818,8 +908,8 @@ empty(Name, #{module := Mod} = Opts) ->
%% newrec/4
-newrec(false = No, _, _, _) ->
- No;
+newrec(none, _, Name, _) ->
+ Name;
newrec(record, Mod, Name, T)
when T /= decode ->
diff --git a/lib/diameter/src/base/diameter_reg.erl b/lib/diameter/src/base/diameter_reg.erl
index 97e74657bd..9ada36acc5 100644
--- a/lib/diameter/src/base/diameter_reg.erl
+++ b/lib/diameter/src/base/diameter_reg.erl
@@ -19,10 +19,11 @@
%%
%%
-%% The module implements a simple term -> pid registry.
+%% A simple term -> pid registry.
%%
-module(diameter_reg).
+
-behaviour(gen_server).
-export([add/1,
@@ -57,18 +58,18 @@
-type key() :: term().
-type from() :: {pid(), term()}.
+-type rcvr() :: [pid() | term()] %% subscribe
+ | from(). %% wait
-type pattern() :: term().
-record(state, {id = diameter_lib:now(),
- receivers = dict:new()
- :: dict:dict(pattern(), [[pid() | term()]%% subscribe
- | from()]), %% wait
+ notify = #{} :: #{pattern() => [rcvr()]},
monitors = sets:new() :: sets:set(pid())}).
%% The ?TABLE bag contains the Key -> Pid mapping, as {Key, Pid}
%% tuples. Each pid is stored in the monitors set to ensure only one
%% monitor for each pid: more are harmless, but unnecessary. A pattern
-%% is added to receivers a result of calls to wait/1 or subscribe/2:
+%% is added to notify a result of calls to wait/1 or subscribe/2:
%% changes to ?TABLE causes processes to be notified as required.
%% ===========================================================================
@@ -156,7 +157,7 @@ wait(Pat) ->
%% # subscribe(Pat, T)
%%
%% Like match/1, but additionally receive messages of the form
-%% {T, add|remove, {term(), pid()} when associations are added
+%% {T, add|remove, {term(), pid()}} when associations are added
%% or removed.
%% ===========================================================================
@@ -186,15 +187,12 @@ uptime() ->
-> [{pid(), [key()]}].
pids() ->
- to_list(fun swap/1).
-
-to_list(Fun) ->
- ets:foldl(fun(T,D) -> append(Fun(T), D) end, orddict:new(), ?TABLE).
-
-append({K,V}, Dict) ->
- orddict:append(K, V, Dict).
+ append(ets:select(?TABLE, [{{'$1','$2'}, [], [{{'$2', '$1'}}]}])).
-id(T) -> T.
+append(Pairs) ->
+ dict:to_list(lists:foldl(fun({K,V}, D) -> dict:append(K, V, D) end,
+ dict:new(),
+ Pairs)).
%% terms/0
@@ -202,9 +200,7 @@ id(T) -> T.
-> [{key(), [pid()]}].
terms() ->
- to_list(fun id/1).
-
-swap({X,Y}) -> {Y,X}.
+ append(ets:tab2list(?TABLE)).
%% subs/0
@@ -212,31 +208,19 @@ swap({X,Y}) -> {Y,X}.
-> [{pattern(), [{pid(), term()}]}].
subs() ->
- #state{receivers = RD} = state(),
- dict:fold(fun sub/3, orddict:new(), RD).
-
-sub(Pat, Ps, Dict) ->
- lists:foldl(fun([P|T], D) -> orddict:append(Pat, {P,T}, D);
- (_, D) -> D
- end,
- Dict,
- Ps).
+ #state{notify = Dict} = state(),
+ [{K, Ts} || {K,Ps} <- maps:to_list(Dict),
+ Ts <- [[{P,T} || [P|T] <- Ps]]].
%% waits/0
-spec waits()
- -> [{pattern(), [{from(), term()}]}].
+ -> [{pattern(), [from()]}].
waits() ->
- #state{receivers = RD} = state(),
- dict:fold(fun wait/3, orddict:new(), RD).
-
-wait(Pat, Ps, Dict) ->
- lists:foldl(fun({_,_} = F, D) -> orddict:append(Pat, F, D);
- (_, D) -> D
- end,
- Dict,
- Ps).
+ #state{notify = Dict} = state(),
+ [{K, Ts} || {K,Ps} <- maps:to_list(Dict),
+ Ts <- [[T || {_,_} = T <- Ps]]].
%% ----------------------------------------------------------
%% # init/1
@@ -250,33 +234,28 @@ init(_) ->
%% # handle_call/3
%% ----------------------------------------------------------
-handle_call({add, Uniq, Key}, {Pid, _}, S0) ->
+handle_call({add, Uniq, Key}, {Pid, _}, S) ->
Rec = {Key, Pid},
- S1 = flush(Uniq, Rec, S0),
+ NS = flush(Uniq, Rec, S), %% before insert
{Res, New} = insert(Uniq, Rec),
- {Recvs, S} = add(New, Rec, S1),
- notify(Recvs, Rec),
- {reply, Res, S};
+ {reply, Res, notify(add, New andalso Rec, NS)};
handle_call({remove, Key}, {Pid, _}, S) ->
Rec = {Key, Pid},
- Recvs = delete([Rec], S),
ets:delete_object(?TABLE, Rec),
- notify(Recvs, remove),
- {reply, true, S};
+ {reply, true, notify(remove, Rec, S)};
-handle_call({wait, Pat}, {Pid, _} = From, #state{receivers = RD} = S) ->
+handle_call({wait, Pat}, {Pid, _} = From, S) ->
NS = add_monitor(Pid, S),
case match(Pat) of
- [_|_] = L ->
- {reply, L, NS};
+ [_|_] = Recs ->
+ {reply, Recs, NS};
[] ->
- {noreply, NS#state{receivers = dict:append(Pat, From, RD)}}
+ {noreply, queue(Pat, From, NS)}
end;
-handle_call({subscribe, Pat, T}, {Pid, _}, #state{receivers = RD} = S) ->
- NS = add_monitor(Pid, S),
- {reply, match(Pat), NS#state{receivers = dict:append(Pat, [Pid | T], RD)}};
+handle_call({subscribe, Pat, T}, {Pid, _}, S) ->
+ {reply, match(Pat), queue(Pat, [Pid | T], add_monitor(Pid, S))};
handle_call(state, _, S) ->
{reply, S, S};
@@ -332,106 +311,62 @@ insert(true, Rec) ->
B = ets:insert_new(?TABLE, Rec), %% entry inserted?
{B, B}.
-%% add/3
-
+%% add_monitor/2
+%%
%% Only add a single monitor for any given process, since there's no
%% use to more.
-add(true, {_Key, Pid} = Rec, S) ->
- NS = add_monitor(Pid, S),
- {Recvs, RD} = add(Rec, NS),
- {Recvs, S#state{receivers = RD}};
-
-add(false = No, _, S) ->
- {No, S}.
-
-%% add/2
-
-%% Notify processes whose patterns match the inserted key.
-add({_Key, Pid} = Rec, #state{receivers = RD}) ->
- dict:fold(fun(Pt, Ps, A) ->
- add(lists:member(Rec, match(Pt, Pid)), Pt, Ps, Rec, A)
- end,
- {sets:new(), RD},
- RD).
-
-%% add/5
-add(true, Pat, Recvs, {_,_} = Rec, {Set, Dict}) ->
- {lists:foldl(fun sets:add_element/2, Set, Recvs),
- remove(fun erlang:is_list/1, Pat, Recvs, Dict)};
-
-add(false, _, _, _, Acc) ->
+add_monitor(Pid, #state{monitors = Ps} = S) ->
+ case sets:is_element(Pid, Ps) of
+ false ->
+ monitor(process, Pid),
+ S#state{monitors = sets:add_element(Pid, Ps)};
+ true ->
+ S
+ end.
+
+%% notify/3
+
+notify(_, false, S) ->
+ S;
+
+notify(Op, {_,_} = Rec, #state{notify = Dict} = S) ->
+ S#state{notify = maps:fold(fun(P,Rs,D) -> notify(Op, Rec, P, Rs, D) end,
+ Dict,
+ Dict)}.
+
+%% notify/5
+
+notify(Op, {_, Pid} = Rec, Pat, Rcvrs, Dict) ->
+ case lists:member(Rec, match(Pat, Pid)) of
+ true ->
+ reset(Pat, Dict, lists:foldr(fun(P,A) -> send(P, Op, Rec, A) end,
+ [],
+ Rcvrs));
+ false ->
+ Dict
+ end.
+
+%% send/4
+
+send([Pid | T] = Rcvr, Op, Rec, Acc) ->
+ Pid ! {T, Op, Rec},
+ [Rcvr | Acc];
+
+%% No processes wait on remove: they receive notification immediately
+%% or at add, by construction.
+send({_,_} = From, add, Rec, Acc) ->
+ gen_server:reply(From, [Rec]),
Acc.
-%% add_monitor/2
-
-add_monitor(Pid, #state{monitors = MS} = S) ->
- add_monitor(sets:is_element(Pid, MS), Pid, S).
-
-%% add_monitor/3
-
-add_monitor(false, Pid, #state{monitors = MS} = S) ->
- monitor(process, Pid),
- S#state{monitors = sets:add_element(Pid, MS)};
-
-add_monitor(true, _, S) ->
- S.
-
-%% delete/2
-
-delete(Recs, #state{receivers = RD}) ->
- lists:foldl(fun(R,S) -> delete(R, RD, S) end, sets:new(), Recs).
-
-%% delete/3
-
-delete({_Key, Pid} = Rec, RD, Set) ->
- dict:fold(fun(Pt, Ps, S) ->
- delete(lists:member(Rec, match(Pt, Pid)), Rec, Ps, S)
- end,
- Set,
- RD).
-
-%% delete/4
-
-%% Entry matches a pattern ...
-delete(true, Rec, Recvs, Set) ->
- lists:foldl(fun(R,S) -> sets:add_element({R, Rec}, S) end,
- Set,
- Recvs);
-
-%% ... or not.
-delete(false, _, _, Set) ->
- Set.
-
-%% notify/2
-
-notify(false = No, _) ->
- No;
-
-notify(Recvs, remove = Op) ->
- sets:fold(fun({P,R}, N) -> send(P, R, Op), N+1 end, 0, Recvs);
-
-notify(Recvs, {_,_} = Rec) ->
- sets:fold(fun(P,N) -> send(P, Rec, add), N+1 end, 0, Recvs).
-
-%% send/3
-
-%% No processes waiting on remove, by construction: they've either
-%% received notification at add or aren't waiting.
-send([Pid | T], Rec, Op) ->
- Pid ! {T, Op, Rec};
-
-send({_,_} = From, Rec, add) ->
- gen_server:reply(From, [Rec]).
-
%% down/2
-down(Pid, #state{monitors = MS} = S) ->
- NS = flush(Pid, S),
- Recvs = delete(match('_', Pid), NS),
+down(Pid, #state{monitors = Ps} = S) ->
+ Recs = match('_', Pid),
ets:match_delete(?TABLE, {'_', Pid}),
- notify(Recvs, remove),
- NS#state{monitors = sets:del_element(Pid, MS)}.
+ lists:foldl(fun(R,NS) -> notify(remove, R, NS) end,
+ flush(Pid, S#state{monitors = sets:del_element(Pid, Ps)}),
+ Recs).
%% flush/3
@@ -452,16 +387,15 @@ flush(false, _, S) ->
%% flush/2
%% Process has died and should no longer receive messages/replies.
-flush(Pid, #state{receivers = RD} = S)
- when is_pid(Pid) ->
- S#state{receivers = dict:fold(fun(Pt,Ps,D) -> flush(Pid, Pt, Ps, D) end,
- RD,
- RD)}.
+flush(Pid, #state{notify = Dict} = S) ->
+ S#state{notify = maps:fold(fun(P,Rs,D) -> flush(Pid, P, Rs, D) end,
+ Dict,
+ Dict)}.
%% flush/4
-flush(Pid, Pat, Recvs, Dict) ->
- remove(fun(T) -> Pid /= head(T) end, Pat, Recvs, Dict).
+flush(Pid, Pat, Rcvrs, Dict) ->
+ reset(Pat, Dict, [T || T <- Rcvrs, Pid /= head(T)]).
%% head/1
@@ -471,15 +405,18 @@ head([P|_]) ->
head({P,_}) ->
P.
-%% remove/4
+%% reset/3
+
+reset(Key, Map, []) ->
+ maps:remove(Key, Map);
+
+reset(Key, Map, List) ->
+ maps:put(Key, List, Map).
+
+%% queue/3
-remove(Pred, Key, Values, Dict) ->
- case lists:filter(Pred, Values) of
- [] ->
- dict:erase(Key, Dict);
- Rest ->
- dict:store(Key, Rest, Dict)
- end.
+queue(Pat, Rcvr, #state{notify = Dict} = S) ->
+ S#state{notify = maps:put(Pat, [Rcvr | maps:get(Pat, Dict, [])], Dict)}.
%% call/1
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 802e6e7243..31dd92f878 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -115,6 +115,7 @@
strict_arities => diameter:strict_arities(),
strict_mbit := boolean(),
decode_format := diameter:decode_format(),
+ avp_dictionaries => nonempty_list(module()),
traffic_counters := boolean(),
string_decode := boolean(),
capabilities_cb => diameter:evaluable(),
@@ -725,9 +726,14 @@ init_peers() ->
%% TPid}
service_opts(Opts) ->
- T = {strict_arities, true},
- maps:merge(maps:from_list([{monitor, false} | def_opts() -- [T]]),
- maps:from_list(Opts -- [T])).
+ remove([{strict_arities, true},
+ {avp_dictionaries, []}],
+ maps:merge(maps:from_list([{monitor, false} | def_opts()]),
+ maps:from_list(Opts))).
+
+remove(List, Map) ->
+ maps:filter(fun(K,V) -> not lists:member({K,V}, List) end,
+ Map).
def_opts() -> %% defaults on the service map
[{share_peers, false},
@@ -738,6 +744,7 @@ def_opts() -> %% defaults on the service map
{strict_arities, true},
{strict_mbit, true},
{decode_format, record},
+ {avp_dictionaries, []},
{traffic_counters, true},
{string_decode, true},
{spawn_opt, []}].
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index b89c4648d1..f510f40a17 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -78,6 +78,7 @@
sequence :: diameter:sequence(),
counters :: boolean(),
codec :: #{decode_format := diameter:decode_format(),
+ avp_dictionaries => nonempty_list(module()),
string_decode := boolean(),
strict_arities => diameter:strict_arities(),
strict_mbit := boolean(),
@@ -108,6 +109,7 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) ->
sequence = Mask,
counters = B,
codec = maps:with([decode_format,
+ avp_dictionaries,
string_decode,
strict_arities,
strict_mbit,
@@ -352,6 +354,8 @@ recv_request(Ack,
No
end.
+%% decode/4
+
decode(Id, Dict, #recvdata{codec = Opts}, Pkt) ->
errors(Id, diameter_codec:decode(Id, Dict, Opts, Pkt)).
@@ -1032,15 +1036,15 @@ answer_message(RC,
origin_realm = {OR,_}},
#diameter_packet{avps = Avps,
errors = Es}) ->
- {Code, _, Vid} = Dict0:avp_header('Session-Id'),
['answer-message', {'Origin-Host', OH},
{'Origin-Realm', OR},
- {'Result-Code', RC}]
- ++ session_id(Code, Vid, Avps)
- ++ failed_avp(RC, Es).
+ {'Result-Code', RC}
+ | session_id(Dict0, Avps)
+ ++ failed_avp(RC, Es)
+ ++ proxy_info(Dict0, Avps)].
-session_id(Code, Vid, Avps)
- when is_list(Avps) ->
+session_id(Dict0, Avps) ->
+ {Code, _, Vid} = Dict0:avp_header('Session-Id'),
try
#diameter_avp{data = Bin} = find_avp(Code, Vid, Avps),
[{'Session-Id', [Bin]}]
@@ -1058,6 +1062,14 @@ failed_avp(RC, [_ | Es]) ->
failed_avp(_, [] = No) ->
No.
+proxy_info(Dict0, Avps) ->
+ {Code, _, Vid} = Dict0:avp_header('Proxy-Info'),
+ [{'AVP', [A#diameter_avp{value = undefined}
+ || [#diameter_avp{code = C, vendor_id = I} = A | _]
+ <- Avps,
+ C == Code,
+ I == Vid]}].
+
%% find_avp/3
%% Grouped ...
@@ -1892,16 +1904,12 @@ str(T) ->
%% get_avp/3
%%
-%% Find an AVP in a message of one of three forms:
-%%
-%% - a message record (as generated from a .dia spec) or
-%% - a list of an atom message name followed by 2-tuple, avp name/value pairs.
-%% - a list of a #diameter_header{} followed by #diameter_avp{} records,
-%%
-%% In the first two forms a dictionary module is used at encode to
-%% identify the type of the AVP and its arity in the message in
-%% question. The third form allows messages to be sent as is, without
-%% a dictionary, which is needed in the case of relay agents, for one.
+%% Find an AVP in a message in one of the decoded formats, or as a
+%% header/avps list. There are only four AVPs that are extracted here:
+%% Result-Code and Experimental-Result in order when constructing
+%% counter keys, and Destination-Host/Realm when selecting a next-hop
+%% peer. Experimental-Result is the only of type Grouped, and is given
+%% special treatment in order to return the value as a record.
%% Messages will be header/avps list as a relay and the only AVP's we
%% look for are in the common dictionary. This is required since the
@@ -1910,12 +1918,12 @@ str(T) ->
get_avp(?RELAY, Name, Msg) ->
get_avp(?BASE, Name, Msg);
-%% Message is a header/avps list.
+%% Message as header/avps list.
get_avp(Dict, Name, [#diameter_header{} | Avps]) ->
try
- {Code, _, VId} = Dict:avp_header(Name),
- A = find_avp(Code, VId, Avps),
- (avp_decode(Dict, Name, ungroup(A)))#diameter_avp{name = Name}
+ {Code, _, Vid} = Dict:avp_header(Name),
+ A = find_avp(Code, Vid, Avps),
+ avp_decode(Dict, Name, ungroup(A))
catch
error: _ ->
undefined
@@ -1925,20 +1933,33 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) ->
get_avp(_, Name, [_MsgName | Avps]) ->
case find(Name, Avps) of
{_, V} ->
- #diameter_avp{name = Name, value = V};
+ #diameter_avp{name = Name, value = value(Name, V)};
_ ->
undefined
end;
-%% ... or record (but not necessarily).
+%% ... or record.
get_avp(Dict, Name, Rec) ->
- try
- #diameter_avp{name = Name, value = Dict:'#get-'(Name, Rec)}
+ try Dict:'#get-'(Name, Rec) of
+ V ->
+ #diameter_avp{name = Name, value = value(Name, V)}
catch
error:_ ->
undefined
end.
+value('Experimental-Result' = N, #{'Vendor-Id' := Vid,
+ 'Experimental-Result-Code' := RC}) ->
+ {N, Vid, RC};
+value('Experimental-Result' = N, [{'Experimental-Result-Code', RC},
+ {'Vendor-Id', Vid}]) ->
+ {N, Vid, RC};
+value('Experimental-Result' = N, [{'Vendor-Id', Vid},
+ {'Experimental-Result-Code', RC}]) ->
+ {N, Vid, RC};
+value(_, V) ->
+ V.
+
%% find/2
find(Key, Map)
@@ -1968,14 +1989,25 @@ ungroup(Avp) ->
%% avp_decode/3
+%% Ensure Experimental-Result is decoded as record, since this format
+%% is used for counter keys.
+avp_decode(Dict, 'Experimental-Result' = N, #diameter_avp{data = Bin}
+ = Avp)
+ when is_binary(Bin) ->
+ {V,_} = Dict:avp(decode, Bin, N, decode_opts(Dict)),
+ Avp#diameter_avp{name = N, value = V};
+
avp_decode(Dict, Name, #diameter_avp{value = undefined,
data = Bin}
= Avp)
when is_binary(Bin) ->
V = Dict:avp(decode, Bin, Name, decode_opts(Dict)),
- Avp#diameter_avp{value = V};
-avp_decode(_, _, #diameter_avp{} = Avp) ->
- Avp.
+ Avp#diameter_avp{name = Name, value = V};
+
+avp_decode(_, Name, #diameter_avp{} = Avp) ->
+ Avp#diameter_avp{name = Name}.
+
+%% cb/3
cb(#diameter_app{module = [_|_] = M}, F, A) ->
eval(M, F, A).
@@ -1992,4 +2024,5 @@ decode_opts(Dict) ->
string_decode => false,
strict_mbit => false,
failed_avp => false,
- dictionary => Dict}.
+ module => Dict,
+ app_dictionary => Dict}.
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index bb671e9860..c08e2da672 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -72,7 +72,7 @@
restrict := boolean(),
suspect := non_neg_integer(), %% OKAY -> SUSPECT
okay := non_neg_integer()}, %% REOPEN -> OKAY
- codec :: #{decode_format := false,
+ codec :: #{decode_format := none,
string_decode := false,
strict_arities => diameter:strict_arities(),
strict_mbit := boolean(),
@@ -157,7 +157,7 @@ i({Ack, T, Pid, {Opts,
string_decode,
rfc,
ordered_encode],
- SvcOpts#{decode_format := false,
+ SvcOpts#{decode_format := none,
string_decode := false,
ordered_encode => false})}.
diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl
index f9f2b02e94..7b53e51cb6 100644
--- a/lib/diameter/src/compiler/diameter_dict_util.erl
+++ b/lib/diameter/src/compiler/diameter_dict_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -923,7 +923,7 @@ xa([D|_] = Ds, [[Qual, D, {_, Line, AvpName}] | Avps], Dict, Key, Name) ->
store_new({Key, {Name, AvpName}},
[Line, Qual, D],
Dict,
- [Name, Line],
+ [AvpName, Line],
avp_already_referenced),
Key,
Name);
diff --git a/lib/diameter/src/dict/doic_rfc7683.dia b/lib/diameter/src/dict/doic_rfc7683.dia
new file mode 100644
index 0000000000..2b7804115e
--- /dev/null
+++ b/lib/diameter/src/dict/doic_rfc7683.dia
@@ -0,0 +1,50 @@
+;;
+;; %CopyrightBegin%
+;;
+;; Copyright Ericsson AB 2017. All Rights Reserved.
+;;
+;; Licensed under the Apache License, Version 2.0 (the "License");
+;; you may not use this file except in compliance with the License.
+;; You may obtain a copy of the License at
+;;
+;; http://www.apache.org/licenses/LICENSE-2.0
+;;
+;; Unless required by applicable law or agreed to in writing, software
+;; distributed under the License is distributed on an "AS IS" BASIS,
+;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;; See the License for the specific language governing permissions and
+;; limitations under the License.
+;;
+;; %CopyrightEnd%
+;;
+
+@name diameter_gen_doic_rfc7683
+@prefix diameter_doic
+
+@avp_types
+
+ OC-Supported-Features 621 Grouped -
+ OC-Feature-Vector 622 Unsigned64 -
+ OC-OLR 623 Grouped -
+ OC-Sequence-Number 624 Unsigned64 -
+ OC-Validity-Duration 625 Unsigned32 -
+ OC-Report-Type 626 Enumerated -
+ OC-Reduction-Percentage 627 Unsigned32 -
+
+@enum OC-Report-Type
+
+ HOST_REPORT 0
+ REALM_REPORT 1
+
+@grouped
+
+ OC-Supported-Features ::= < AVP Header: 621 >
+ [ OC-Feature-Vector ]
+ * [ AVP ]
+
+ OC-OLR ::= < AVP Header: 623 >
+ < OC-Sequence-Number >
+ < OC-Report-Type >
+ [ OC-Reduction-Percentage ]
+ [ OC-Validity-Duration ]
+ * [ AVP ]
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index bb3b234d20..bb86de016a 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -24,6 +24,7 @@ DICTS = \
base_rfc6733 \
base_accounting \
acct_rfc6733 \
+ doic_rfc7683 \
relay
# The yecc grammar for the dictionary parser.
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index 8de19e904e..4eb3379d59 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -102,6 +102,7 @@
streams :: {uint(), uint()} %% {InStream, OutStream} counts
| undefined,
os = 0 :: uint(), %% next output stream
+ rotate = 1 :: boolean() | 0 | 1, %% rotate os?
packet = true :: boolean() %% legacy transport_data?
| raw,
message_cb = false :: false | diameter:eval(),
@@ -692,11 +693,16 @@ send(#diameter_packet{transport_data = {outstream, SId}}
= S) ->
send(SId rem OS, Msg, S);
-%% ... or not: rotate through all streams.
-send(Msg, #transport{streams = {_, OS},
+%% ... or not: rotate when sending on multiple streams ...
+send(Msg, #transport{rotate = true,
+ streams = {_, OS},
os = N}
= S) ->
- send(N, Msg, S#transport{os = (N + 1) rem OS}).
+ send(N, Msg, S#transport{os = (N + 1) rem OS});
+
+%% ... or send on the only stream available.
+send(Msg, S) ->
+ send(0, Msg, S).
%% send/3
@@ -764,7 +770,7 @@ recv({[#sctp_sndrcvinfo{assoc_id = Id}], _Bin}
%% Inbound Diameter message.
recv({[#sctp_sndrcvinfo{}], Bin} = Msg, S)
when is_binary(Bin) ->
- message(recv, Msg, S);
+ message(recv, Msg, recv(S));
recv({_, #sctp_shutdown_event{}}, _) ->
stop;
@@ -784,6 +790,25 @@ recv({_, #sctp_paddr_change{}}, _) ->
recv({_, #sctp_pdapi_event{}}, _) ->
ok.
+%% recv/1
+%%
+%% Start sending unordered after the second reception, so that an
+%% outgoing CER/CEA will arrive at the peer before another request.
+
+recv(#transport{rotate = B} = S)
+ when is_boolean(B) ->
+ S;
+
+recv(#transport{rotate = 0, streams = {_,N}, socket = Sock} = S) ->
+ ok = inet:setopts(Sock, [{sctp_default_send_param,
+ #sctp_sndrcvinfo{flags = [unordered]}}]),
+ S#transport{rotate = 1 < N};
+
+recv(#transport{rotate = N} = S) ->
+ S#transport{rotate = N-1}.
+
+%% publish/4
+
publish(T, Ref, Id, Sock) ->
true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}}),
putr(?INFO_KEY, {gen_sctp, Sock}). %% for info/1
diff --git a/lib/diameter/test/diameter_codec_SUITE.erl b/lib/diameter/test/diameter_codec_SUITE.erl
index c79b642c09..17112794e4 100644
--- a/lib/diameter/test/diameter_codec_SUITE.erl
+++ b/lib/diameter/test/diameter_codec_SUITE.erl
@@ -291,7 +291,7 @@ recode(Msg, Dict) ->
recode(#diameter_packet{msg = Msg}, Dict).
opts(Mod) ->
- #{dictionary => Mod,
+ #{app_dictionary => Mod,
decode_format => record,
string_decode => false,
strict_mbit => true,
diff --git a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl
index 735339ebb9..c6bba75f09 100644
--- a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl
+++ b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl
@@ -77,7 +77,7 @@ dec('BR', #diameter_packet
ok.
opts(Mod) ->
- #{dictionary => Mod,
+ #{app_dictionary => Mod,
decode_format => record,
string_decode => true,
strict_mbit => true,
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index 22fb0550ea..70e910ffa6 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.erl
@@ -44,7 +44,8 @@ base() ->
[] = run([[fun base/1, T] || T <- [zero, decode]]).
gen(Mod) ->
- Fs = [{Mod, F, []} || F <- [name, id, vendor_id, vendor_name]],
+ Fs = [{Mod, F, []} || Mod /= diameter_gen_doic_rfc7683,
+ F <- [name, id, vendor_id, vendor_name]],
[] = run(Fs ++ [[fun gen/2, Mod, T] || T <- [messages,
command_codes,
avp_types,
@@ -216,7 +217,7 @@ avp(Mod, encode = X, V, Name, _) ->
opts(Mod) ->
(opts())#{module => Mod,
- dictionary => Mod}.
+ app_dictionary => Mod}.
opts() ->
#{decode_format => record,
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 6f30055a0a..ffb4a508cd 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -20,6 +20,7 @@
%%
%% Tests of traffic between two Diameter nodes, one client, one server.
+%% The traffic isn't meant to be sensible, just to exercise code.
%%
-module(diameter_traffic_SUITE).
@@ -49,6 +50,7 @@
send_protocol_error/1,
send_experimental_result/1,
send_arbitrary/1,
+ send_proxy_info/1,
send_unknown/1,
send_unknown_short/1,
send_unknown_mandatory/1,
@@ -114,6 +116,8 @@
%% diameter_{tcp,sctp} callbacks
-export([message/3]).
+-include_lib("kernel/include/inet_sctp.hrl").
+
-include("diameter.hrl").
-include("diameter_gen_base_rfc3588.hrl").
-include("diameter_gen_base_accounting.hrl").
@@ -135,6 +139,7 @@
-define(A, list_to_atom).
-define(L, atom_to_list).
+-define(B, iolist_to_binary).
%% Don't use is_record/2 since dictionary hrl's aren't included.
%% (Since they define conflicting records with the same names.)
@@ -154,7 +159,7 @@
-define(ENCODINGS, [list, record, map]).
%% How to decode incoming messages.
--define(DECODINGS, [record, false, map, list, record_from_map]).
+-define(DECODINGS, [record, none, map, list, record_from_map]).
%% Which dictionary to use in the clients.
-define(RFCS, [rfc3588, rfc6733, rfc4005]).
@@ -213,6 +218,7 @@
{'Acct-Application-Id', [3]}, %% base accounting
{restrict_connections, false},
{string_decode, Grp#group.strings},
+ {avp_dictionaries, [diameter_gen_doic_rfc7683]},
{incoming_maxlen, 1 bsl 21}
| [{application, [{dictionary, D},
{module, [?MODULE, Grp]},
@@ -442,6 +448,7 @@ tc() ->
send_protocol_error,
send_experimental_result,
send_arbitrary,
+ send_proxy_info,
send_unknown,
send_unknown_short,
send_unknown_mandatory,
@@ -507,6 +514,7 @@ start_services(Config) ->
| ?SERVICE(SN, Grp)]),
ok = diameter:start_service(CN, [{traffic_counters, bool()},
{sequence, ?CLIENT_MASK},
+ {decode_format, map},
{strict_arities, decode}
| ?SERVICE(CN, Grp)]).
@@ -533,7 +541,7 @@ add_transports(Config) ->
| server_apps()]
++ [{spawn_opt, {erlang, spawn, []}} || CS]),
Cs = [?util:connect(CN,
- [T, {sender, CS}],
+ [T, {sender, CS} | client_opts(T)],
LRef,
[{id, Id}
| client_apps(R, [{'Origin-State-Id', origin(Id)}])])
@@ -543,6 +551,14 @@ add_transports(Config) ->
Id <- [{D,E}]],
?util:write_priv(Config, "transport", [LRef | Cs]).
+client_opts(tcp) ->
+ [];
+client_opts(sctp) ->
+ [{sctp_initmsg, #sctp_initmsg{num_ostreams = N,
+ max_instreams = 5}}
+ || N <- [rand:uniform(8)],
+ N =< 6].
+
server_apps() ->
B = have_nas(),
[{applications, [diameter_gen_base_rfc3588,
@@ -624,7 +640,6 @@ result_codes(_Config) ->
send_ok(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 1}],
-
['ACA' | #{'Result-Code' := ?SUCCESS,
'Session-Id' := _}]
= call(Config, Req).
@@ -656,13 +671,80 @@ send_bad_answer(Config) ->
= call(Config, Req).
%% Send an ACR that the server callback answers explicitly with a
-%% protocol error.
+%% protocol error and some AVPs to check the decoding of.
send_protocol_error(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 4}],
- ?answer_message(?TOO_BUSY)
- = call(Config, Req).
+ ['answer-message' | #{'Result-Code' := ?TOO_BUSY,
+ 'AVP' := [OLR | _]} = Avps]
+ = call(Config, Req),
+
+ #diameter_avp{name = 'OC-OLR',
+ value = #{'OC-Sequence-Number' := 1,
+ 'OC-Report-Type' := 0, %% HOST_REPORT
+ 'OC-Reduction-Percentage' := [25],
+ 'OC-Validity-Duration' := [60],
+ 'AVP' := [OSF]}}
+ = OLR,
+ #diameter_avp{name = 'OC-Supported-Features',
+ value = #{} = Fs}
+ = OSF,
+ 0 = maps:size(Fs),
+
+ #group{client_dict = D} = group(Config),
+
+ if D == nas4005 ->
+ error = maps:find('Failed-AVP', Avps),
+ #{'AVP' := [_,Failed]}
+ = Avps,
+ #diameter_avp{name = 'Failed-AVP',
+ value = #{'AVP' := [NP,FR,AP]}}
+ = Failed,
+ #diameter_avp{name = 'NAS-Port',
+ value = 44}
+ = NP,
+ #diameter_avp{name = 'Firmware-Revision',
+ value = 12}
+ = FR,
+ #diameter_avp{name = 'Auth-Grace-Period',
+ value = 13}
+ = AP;
+
+ D == diameter_gen_base_rfc3588;
+ D == diameter_gen_basr_accounting ->
+ error = maps:find('Failed-AVP', Avps),
+ #{'AVP' := [_,Failed]}
+ = Avps,
+
+ #diameter_avp{name = 'Failed-AVP',
+ value = #{'AVP' := [NP,FR,AP]}}
+ = Failed,
+ #diameter_avp{name = undefined,
+ value = undefined}
+ = NP,
+ #diameter_avp{name = 'Firmware-Revision',
+ value = 12}
+ = FR,
+ #diameter_avp{name = 'Auth-Grace-Period',
+ value = 13}
+ = AP;
+
+ D == diameter_gen_base_rfc6733;
+ D == diameter_gen_acct_rfc6733 ->
+ #{'Failed-AVP' := [#{'AVP' := [NP,FR,AP]}],
+ 'AVP' := [_]}
+ = Avps,
+ #diameter_avp{name = undefined,
+ value = undefined}
+ = NP,
+ #diameter_avp{name = 'Firmware-Revision',
+ value = 12}
+ = FR,
+ #diameter_avp{name = 'Auth-Grace-Period',
+ value = 13}
+ = AP
+ end.
%% Send a 3xxx Experimental-Result in an answer not setting the E-bit
%% and missing a Result-Code.
@@ -684,6 +766,19 @@ send_arbitrary(Config) ->
= call(Config, Req),
"XXX" = string(V, Config).
+%% Send Proxy-Info in an ASR that the peer answers with 3xxx, and
+%% ensure that the AVP is returned.
+send_proxy_info(Config) ->
+ H0 = ?B(?util:unique_string()),
+ S0 = ?B(?util:unique_string()),
+ Req = ['ASR', {'Proxy-Info', #{'Proxy-Host' => H0,
+ 'Proxy-State' => S0}}],
+ ['answer-message' | #{'Result-Code' := 3999,
+ 'Proxy-Info' := [#{'Proxy-Host' := H,
+ 'Proxy-State' := S}]}]
+ = call(Config, Req),
+ [H0, S0] = [?B(X) || X <- [H,S]].
+
%% Send an unknown AVP (to some client) and check that it comes back.
send_unknown(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
@@ -707,12 +802,12 @@ send_unknown_short(Config, M, RC) ->
data = <<17>>}]}],
['ASA' | #{'Session-Id' := _,
'Result-Code' := RC,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{code = 999,
- is_mandatory = M,
- data = <<17, _/binary>>}]] %% extra bits from padding
- = failed_avps(Avps, Config).
+ #diameter_avp{code = 999,
+ is_mandatory = M,
+ data = <<17, _/binary>>} %% extra bits from padding
+ = Avp.
%% Ditto but set the M flag.
send_unknown_mandatory(Config) ->
@@ -721,12 +816,12 @@ send_unknown_mandatory(Config) ->
data = <<17>>}]}],
['ASA' | #{'Session-Id' := _,
'Result-Code' := ?AVP_UNSUPPORTED,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{code = 999,
- is_mandatory = true,
- data = <<17>>}]]
- = failed_avps(Avps, Config).
+ #diameter_avp{code = 999,
+ is_mandatory = true,
+ data = <<17>>}
+ = Avp.
%% Ditto, and point the AVP length past the end of the message. Expect
%% 5014 instead of 5001.
@@ -741,13 +836,13 @@ send_unexpected_mandatory_decode(Config) ->
data = <<12:32>>}]}],
['ASA' | #{'Session-Id' := _,
'Result-Code' := ?AVP_UNSUPPORTED,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{code = 27,
- is_mandatory = true,
- value = 12,
- data = <<12:32>>}]]
- = failed_avps(Avps, Config).
+ #diameter_avp{code = 27,
+ is_mandatory = true,
+ value = 12,
+ data = <<12:32>>}
+ = Avp.
%% Try to two Auth-Application-Id in ASR expect 5009.
send_too_many(Config) ->
@@ -755,11 +850,11 @@ send_too_many(Config) ->
['ASA' | #{'Session-Id' := _,
'Result-Code' := ?TOO_MANY,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{name = 'Auth-Application-Id',
- value = 44}]]
- = failed_avps(Avps, Config).
+ #diameter_avp{name = 'Auth-Application-Id',
+ value = 44}
+ = Avp.
%% Send an containing a faulty Grouped AVP (empty Proxy-Host in
%% Proxy-Info) and expect that only the faulty AVP is sent in
@@ -771,12 +866,11 @@ send_grouped_error(Config) ->
{'Proxy-State', ""}]]}],
['ASA' | #{'Session-Id' := _,
'Result-Code' := ?INVALID_AVP_LENGTH,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{name = 'Proxy-Info', value = V}]]
- = failed_avps(Avps, Config),
- {Empty, undefined, []} = proxy_info(V, Config),
- <<0>> = iolist_to_binary(Empty).
+ #diameter_avp{name = 'Proxy-Info', value = #{'Proxy-Host' := H}}
+ = Avp,
+ <<0>> = ?B(H).
%% Send an STR that the server ignores.
send_noreply(Config) ->
@@ -829,9 +923,8 @@ send_invalid_avp_length(Config) ->
'Result-Code' := ?INVALID_AVP_LENGTH,
'Origin-Host' := _,
'Origin-Realm' := _,
- 'Failed-AVP' := Avps}]
- = call(Config, Req),
- [[_]] = failed_avps(Avps, Config).
+ 'Failed-AVP' := [#{'AVP' := [_]}]}]
+ = call(Config, Req).
%% Send a request containing 5xxx errors that the server rejects with
%% 3xxx.
@@ -1042,29 +1135,6 @@ send_anything(Config) ->
%% ===========================================================================
-failed_avps(Avps, Config) ->
- #group{client_dict = D} = proplists:get_value(group, Config),
- [failed_avp(D, T) || T <- Avps].
-
-failed_avp(nas4005, {'nas_Failed-AVP', As}) ->
- As;
-failed_avp(_, #'diameter_base_Failed-AVP'{'AVP' = As}) ->
- As.
-
-proxy_info(Rec, Config) ->
- #group{client_dict = D} = proplists:get_value(group, Config),
- if D == nas4005 ->
- {'nas_Proxy-Info', H, S, As}
- = Rec,
- {H,S,As};
- true ->
- #'diameter_base_Proxy-Info'{'Proxy-Host' = H,
- 'Proxy-State' = S,
- 'AVP' = As}
- = Rec,
- {H,S,As}
- end.
-
group(Config) ->
#group{} = proplists:get_value(group, Config).
@@ -1105,12 +1175,12 @@ origin(N) ->
decode(record) -> 0;
decode(list) -> 1;
decode(map) -> 2;
-decode(false) -> 3;
+decode(none) -> 3;
decode(record_from_map) -> 4;
decode(0) -> record;
decode(1) -> list;
decode(2) -> map;
-decode(3) -> false;
+decode(3) -> none;
decode(4) -> record_from_map.
encode(record) -> 0;
@@ -1157,16 +1227,18 @@ to_map(#diameter_packet{header = H, msg = Rec},
%% No record decode: do it ourselves.
to_map(#diameter_packet{header = H,
- msg = false,
+ msg = Name,
bin = Bin},
- #group{server_decoding = false,
+ #group{server_decoding = none,
strings = B}) ->
Opts = #{decode_format => map,
string_decode => B,
+ avp_dictionaries => [diameter_gen_doic_rfc7683],
strict_mbit => true,
rfc => 6733},
- #diameter_packet{msg = [_MsgName | _Map] = Msg}
+ #diameter_packet{msg = [MsgName | _Map] = Msg}
= diameter_codec:decode(dict(H), Opts, Bin),
+ {MsgName, _} = {Name, Msg}, %% assert
Msg.
dict(#diameter_header{application_id = Id,
@@ -1534,24 +1606,23 @@ answer(Pkt, Req, _Peer, Name, #group{client_dict = Dict0}) ->
#diameter_packet{header = H, msg = Ans, errors = Es} = Pkt,
ApplId = app(Req, Name, Dict0),
#diameter_header{application_id = ApplId} = H, %% assert
- Dict = dict(Ans, Dict0),
- rec_to_map(answer(Ans, Es, Name), Dict).
+ answer(Ans, Es, Name).
%% Missing Result-Code and inappropriate Experimental-Result-Code.
-answer(Rec, Es, send_experimental_result) ->
+answer(Ans, Es, send_experimental_result) ->
[{5004, #diameter_avp{name = 'Experimental-Result'}},
{5005, #diameter_avp{name = 'Result-Code'}}]
= Es,
- Rec;
+ Ans;
%% An inappropriate E-bit results in a decode error ...
-answer(Rec, Es, send_bad_answer) ->
+answer(Ans, Es, send_bad_answer) ->
[{5004, #diameter_avp{name = 'Result-Code'}} | _] = Es,
- Rec;
+ Ans;
%% ... while other errors are reflected in Failed-AVP.
-answer(Rec, [], _) ->
- Rec.
+answer(Ans, [], _) ->
+ Ans.
app(_, send_unsupported_app, _) ->
?BAD_APP;
@@ -1728,11 +1799,33 @@ request(['ACR' | #{'Session-Id' := SId,
request(['ACR' | #{'Accounting-Record-Number' := 4}],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
+ %% Include a DOIC AVP that will be encoded/decoded because of
+ %% avp_dictionaries config.
+ OLR = #{'OC-Sequence-Number' => 1,
+ 'OC-Report-Type' => 0, %% HOST_REPORT
+ 'OC-Reduction-Percentage' => [25],
+ 'OC-Validity-Duration' => [60],
+ 'AVP' => [{'OC-Supported-Features', []}]},
+ %% Include a NAS Failed-AVP AVP that will only be decoded under
+ %% that application. Encode as 'AVP' since RFC 3588 doesn't list
+ %% Failed-AVP in the answer-message grammar while RFC 6733 does.
+ NP = #diameter_avp{data = {nas4005, 'NAS-Port', 44}},
+ FR = #diameter_avp{name = 'Firmware-Revision', value = 12}, %% M=0
+ AP = #diameter_avp{name = 'Auth-Grace-Period', value = 13}, %% M=1
+ Failed = #diameter_avp{data = {diameter_gen_base_rfc3588,
+ 'Failed-AVP',
+ [{'AVP', [NP,FR,AP]}]}},
Ans = ['answer-message', {'Result-Code', ?TOO_BUSY},
{'Origin-Host', OH},
- {'Origin-Realm', OR}],
+ {'Origin-Realm', OR},
+ {'AVP', [{'OC-OLR', OLR}, Failed]}],
{reply, Ans};
+%% send_proxy_info
+request(['ASR' | #{'Proxy-Info' := _}],
+ _) ->
+ {protocol_error, 3999};
+
request(['ASR' | #{'Session-Id' := SId} = Avps],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl
index 6e17ec0af0..0e084e619e 100644
--- a/lib/edoc/src/edoc_doclet.erl
+++ b/lib/edoc/src/edoc_doclet.erl
@@ -198,7 +198,7 @@ source({M, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden,
{Set, Error}
end;
R ->
- report("skipping source file '~ts': ~P.", [File, R, 15]),
+ report("skipping source file '~ts': ~tP.", [File, R, 15]),
{Set, true}
end.
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index ebdb0f79f6..d00a283794 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -541,13 +541,13 @@ uri_get_http_1(Result, URI) ->
Reason = inet:format_error(R),
{error, http_errmsg(Reason, URI)};
{ok, R} ->
- Reason = io_lib:format("bad return value ~P", [R, 5]),
+ Reason = io_lib:format("bad return value ~tP", [R, 5]),
{error, http_errmsg(Reason, URI)};
{'EXIT', R} ->
- Reason = io_lib:format("crashed with reason ~w", [R]),
+ Reason = io_lib:format("crashed with reason ~tw", [R]),
{error, http_errmsg(Reason, URI)};
R ->
- Reason = io_lib:format("uncaught throw: ~w", [R]),
+ Reason = io_lib:format("uncaught throw: ~tw", [R]),
{error, http_errmsg(Reason, URI)}
end.
@@ -603,7 +603,7 @@ filename([]) ->
filename(N) when is_atom(N) ->
atom_to_list(N);
filename(N) ->
- report("bad filename: `~P'.", [N, 25]),
+ report("bad filename: `~tP'.", [N, 25]),
exit(error).
%% @private
@@ -1000,7 +1000,7 @@ run_plugin(Name, Key, Default, Fun, Opts) when is_atom(Name) ->
{ok, Value} ->
Value;
R ->
- report("error in ~ts '~w': ~P.", [Name, Module, R, 20]),
+ report("error in ~ts '~w': ~tP.", [Name, Module, R, 20]),
exit(error)
end.
@@ -1009,7 +1009,7 @@ get_plugin(Key, Default, Opts) ->
M when is_atom(M) ->
M;
Other ->
- report("bad value for option '~w': ~P.", [Key, Other, 10]),
+ report("bad value for option '~w': ~tP.", [Key, Other, 10]),
exit(error)
end.
diff --git a/lib/edoc/src/edoc_run.erl b/lib/edoc/src/edoc_run.erl
index c88c6cfd78..50aba0a930 100644
--- a/lib/edoc/src/edoc_run.erl
+++ b/lib/edoc/src/edoc_run.erl
@@ -150,7 +150,7 @@ file(Args) ->
-spec invalid_args(string(), args()) -> no_return().
invalid_args(Where, Args) ->
- report("invalid arguments to ~ts: ~w.", [Where, Args]),
+ report("invalid arguments to ~ts: ~tw.", [Where, Args]),
shutdown_error().
run(F) ->
@@ -159,10 +159,10 @@ run(F) ->
{ok, _} ->
shutdown_ok();
{'EXIT', E} ->
- report("edoc terminated abnormally: ~P.", [E, 10]),
+ report("edoc terminated abnormally: ~tP.", [E, 10]),
shutdown_error();
Thrown ->
- report("internal error: throw without catch in edoc: ~P.",
+ report("internal error: throw without catch in edoc: ~tP.",
[Thrown, 15]),
shutdown_error()
end.
diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl
index ccc3169767..510f9513b2 100644
--- a/lib/edoc/src/edoc_types.erl
+++ b/lib/edoc/src/edoc_types.erl
@@ -107,7 +107,7 @@ to_xml(#t_paren{type = T}, Env) ->
to_xml(#t_nonempty_list{type = T}, Env) ->
{nonempty_list, [wrap_utype(T, Env)]};
to_xml(#t_atom{val = V}, _Env) ->
- {atom, [{value, io_lib:write(V)}], []};
+ {atom, [{value, atom_to_list(V)}], []};
to_xml(#t_integer{val = V}, _Env) ->
{integer, [{value, integer_to_list(V)}], []};
to_xml(#t_integer_range{from = From, to = To}, _Env) ->
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index d863c056e9..a5e277aece 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -1803,7 +1803,7 @@
<!-- Modulesummary -->
<xsl:template match="modulesummary">
<xsl:param name="partnum"/>
- <h3><a name="module-sumary" href="#module-sumary">Module Summary</a></h3>
+ <h3><a name="module-summary" href="#module-summary">Module Summary</a></h3>
<div class="REFBODY module-summary-body">
<xsl:apply-templates>
<xsl:with-param name="partnum" select="$partnum"/>
@@ -1826,7 +1826,7 @@
<!-- Libsummary -->
<xsl:template match="libsummary">
<xsl:param name="partnum"/>
- <h3><a name="library-sumary" href="#library-sumary">Library Summary</a></h3>
+ <h3><a name="library-summary" href="#library-summary">Library Summary</a></h3>
<div class="REFBODY library-summary-body">
<xsl:apply-templates>
<xsl:with-param name="partnum" select="$partnum"/>
diff --git a/lib/erl_docgen/src/docgen_otp_specs.erl b/lib/erl_docgen/src/docgen_otp_specs.erl
index 6c41147e27..126229ecc9 100644
--- a/lib/erl_docgen/src/docgen_otp_specs.erl
+++ b/lib/erl_docgen/src/docgen_otp_specs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -406,7 +406,7 @@ t_var(E) ->
[get_attrval(name, E)].
t_atom(E) ->
- [get_attrval(value, E)].
+ [io_lib:write(list_to_atom(get_attrval(value, E)))].
t_integer(E) ->
[get_attrval(value, E)].
@@ -578,7 +578,7 @@ ot_var(E) ->
{var,0,list_to_atom(get_attrval(name, E))}.
ot_atom(E) ->
- {ok, [{atom,A,Name}], _} = erl_scan:string(get_attrval(value, E), 0),
+ {ok, [{atom,A,Name}], _} = erl_scan:string(lists:flatten(t_atom(E)), 0),
{atom,erl_anno:line(A),Name}.
ot_integer(E) ->
diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl
index aba90b0be1..b0f016a8ea 100644
--- a/lib/et/src/et_collector.erl
+++ b/lib/et/src/et_collector.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -750,7 +750,7 @@ next_iterate(TH, Prev = first, Limit, Fun, Acc) ->
'$end_of_table' ->
Acc;
{'EXIT', _} = Error ->
- io:format("~p(~p): First ~p~n", [?MODULE, ?LINE, Error]),
+ io:format("~p(~p): First ~tp~n", [?MODULE, ?LINE, Error]),
iterate(TH#table_handle.collector_pid, Prev, Limit, Fun, Acc);
First ->
lookup_and_apply(TH, Prev, First, Limit, -1, Fun, Acc)
@@ -761,7 +761,7 @@ next_iterate(TH, Prev = last, Limit, Fun, Acc) ->
'$end_of_table' ->
Acc;
{'EXIT', _} = Error ->
- io:format("~p(~p): Last ~p~n", [?MODULE, ?LINE, Error]),
+ io:format("~p(~p): Last ~tp~n", [?MODULE, ?LINE, Error]),
iterate(TH#table_handle.collector_pid, Prev, Limit, Fun, Acc);
Last ->
lookup_and_apply(TH, Prev, Last, Limit, -1, Fun, Acc)
@@ -773,7 +773,7 @@ next_iterate(TH, Prev, Limit, Fun, Acc) ->
'$end_of_table' ->
Acc;
{'EXIT', _} = Error ->
- io:format("~p(~p): Next ~p -> ~p~n", [?MODULE, ?LINE, Key, Error]),
+ io:format("~p(~p): Next ~tp -> ~tp~n", [?MODULE, ?LINE, Key, Error]),
iterate(TH#table_handle.collector_pid, Prev, Limit, Fun, Acc);
Next ->
lookup_and_apply(TH, Prev, Next, Limit, -1, Fun, Acc)
@@ -785,7 +785,7 @@ prev_iterate(TH, Prev = first, Limit, Fun, Acc) ->
'$end_of_table' ->
Acc;
{'EXIT', _} = Error ->
- io:format("~p(~p): First ~p~n", [?MODULE, ?LINE, Error]),
+ io:format("~p(~p): First ~tp~n", [?MODULE, ?LINE, Error]),
iterate(TH#table_handle.collector_pid, Prev, Limit, Fun, Acc);
First ->
lookup_and_apply(TH, Prev, First, Limit, 1, Fun, Acc)
@@ -796,7 +796,7 @@ prev_iterate(TH, Prev = last, Limit, Fun, Acc) ->
'$end_of_table' ->
Acc;
{'EXIT', _} = Error ->
- io:format("~p(~p): Last ~p~n", [?MODULE, ?LINE, Error]),
+ io:format("~p(~p): Last ~tp~n", [?MODULE, ?LINE, Error]),
iterate(TH#table_handle.collector_pid, Prev, Limit, Fun, Acc);
Last ->
lookup_and_apply(TH, Prev, Last, Limit, 1, Fun, Acc)
@@ -808,7 +808,7 @@ prev_iterate(TH, Prev, Limit, Fun, Acc) ->
'$end_of_table' ->
Acc;
{'EXIT', _} = Error ->
- io:format("~p(~p): Prev ~p -> ~p~n", [?MODULE, ?LINE, Key, Error]),
+ io:format("~p(~p): Prev ~tp -> ~tp~n", [?MODULE, ?LINE, Key, Error]),
iterate(TH#table_handle.collector_pid, Prev, Limit, Fun, Acc);
Next ->
lookup_and_apply(TH, Prev, Next, Limit, 1, Fun, Acc)
@@ -1049,7 +1049,7 @@ handle_call(stop, _From, S) ->
end,
{stop, shutdown, ok, S};
handle_call(Request, From, S) ->
- ok = error_logger:format("~p(~p): handle_call(~p, ~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_call(~tp, ~tp, ~tp)~n",
[?MODULE, self(), Request, From, S]),
reply({error, {bad_request, Request}}, S).
@@ -1061,7 +1061,7 @@ handle_call(Request, From, S) ->
%%----------------------------------------------------------------------
handle_cast(Msg, S) ->
- ok = error_logger:format("~p(~p): handle_cast(~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_cast(~tp, ~tp)~n",
[?MODULE, self(), Msg, S]),
noreply(S).
@@ -1083,18 +1083,18 @@ handle_info({nodeup, Node}, S) ->
S2 = listen_on_trace_port(Node, Port, S),
noreply(S2);
{error, Reason} when Reason =:= already_started->
- ok = error_logger:format("~p(~p): producer ignored(~p:~p):~n ~p~n",
+ ok = error_logger:format("~p(~p): producer ignored(~p:~p):~n ~tp~n",
[?MODULE, self(), Node, Port, Reason]),
S2 = S#state{trace_port = Port + 1},
noreply(S2);
{badrpc, Reason} ->
- ok = error_logger:format("~p(~p): producer ignored(~p:~p):~n ~p~n",
+ ok = error_logger:format("~p(~p): producer ignored(~p:~p):~n ~tp~n",
[?MODULE, self(), Node, Port, Reason]),
S2 = S#state{trace_port = Port + 1},
noreply(S2);
{error, Reason} ->
self() ! {nodeup, Node},
- ok = error_logger:format("~p(~p): producer retry(~p:~p):~n ~p~n",
+ ok = error_logger:format("~p(~p): producer retry(~p:~p):~n ~tp~n",
[?MODULE, self(), Node, Port, Reason]),
S2 = S#state{trace_port = Port + 1},
noreply(S2)
@@ -1125,12 +1125,12 @@ handle_info(Info = {'EXIT', Pid, Reason}, S) ->
opt_unlink(S#state.parent_pid),
{stop, Reason, S};
false ->
- ok = error_logger:format("~p(~p): handle_info(~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_info(~tp, ~tp)~n",
[?MODULE, self(), Info, S]),
noreply(S)
end;
handle_info(Info, S) ->
- ok = error_logger:format("~p(~p): handle_info(~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_info(~tp, ~tp)~n",
[?MODULE, self(), Info, S]),
noreply(S).
@@ -1143,12 +1143,12 @@ listen_on_trace_port(Node, Port, S) ->
S#state{trace_nodes = [Node | S#state.trace_nodes],
trace_port = Port + 1};
{'EXIT', Reason} when Reason =:= already_started->
- ok = error_logger:format("~p(~p): consumer ignored(~p:~p): ~p~n",
+ ok = error_logger:format("~p(~p): consumer ignored(~p:~p): ~tp~n",
[?MODULE, self(), Node, Port, Reason]),
S#state{trace_port = Port + 1};
{'EXIT', Reason} ->
self() ! {nodeup, Node},
- ok = error_logger:format("~p(~p): consumer retry(~p:~p):~n ~p~n",
+ ok = error_logger:format("~p(~p): consumer retry(~p:~p):~n ~tp~n",
[?MODULE, self(), Node, Port, Reason]),
S#state{trace_port = Port + 1}
end.
@@ -1247,7 +1247,7 @@ file_open(F) ->
{ok, _} ->
{ok, Fd};
{repaired, _, _, BadBytes} ->
- ok = error_logger:format("~p: Skipped ~p bad bytes in file: ~p~n",
+ ok = error_logger:format("~p: Skipped ~p bad bytes in file: ~tp~n",
[?MODULE, BadBytes, F#file.name]),
{ok, Fd};
{error,Reason} ->
diff --git a/lib/et/src/et_selector.erl b/lib/et/src/et_selector.erl
index a0297c21d1..35db07cd99 100644
--- a/lib/et/src/et_selector.erl
+++ b/lib/et/src/et_selector.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -208,7 +208,7 @@ parse_event(Mod, Trace) ->
{to, undefined},
{drop, NumberOfDroppedItems}]}};
_ ->
- error_logger:format("~p(~p): Ignoring unknown trace type -> ~p~n~n",
+ error_logger:format("~p(~p): Ignoring unknown trace type -> ~tp~n~n",
[?MODULE, ?LINE, Trace]),
false
end.
@@ -258,7 +258,7 @@ parse_seq_event(Trace, ParsedTS, ReportedTS, Label, Info) ->
{serial, Serial},
{user_info, UserInfo}]}};
_ ->
- error_logger:format("~p(~p): Ignoring unknown trace type -> ~p~n~n",
+ error_logger:format("~p(~p): Ignoring unknown trace type -> ~tp~n~n",
[?MODULE, ?LINE, Trace]),
false
end.
@@ -590,7 +590,7 @@ parse_event(Mod, Trace, ParsedTS, ReportedTS, From, Label, Contents) ->
{to, From},
{gc_items, GcKeyValueList}]}};
_ ->
- error_logger:format("~p(~p): Ignoring unknown trace type -> ~p~n~n",
+ error_logger:format("~p(~p): Ignoring unknown trace type -> ~tp~n~n",
[?MODULE, ?LINE, Trace]),
false
end.
diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl
index 247dd4c7ba..bca517317e 100644
--- a/lib/et/src/et_wx_contents_viewer.erl
+++ b/lib/et/src/et_wx_contents_viewer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -213,7 +213,7 @@ init([S]) when is_record(S, state) ->
%%----------------------------------------------------------------------
handle_call(Request, From, S) ->
- ok = error_logger:format("~p(~p): handle_call(~p, ~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_call(~tp, ~tp, ~tp)~n",
[?MODULE, self(), Request, From, S]),
Reply = {error, {bad_request, Request}},
{reply, Reply, S}.
@@ -226,7 +226,7 @@ handle_call(Request, From, S) ->
%%----------------------------------------------------------------------
handle_cast(Msg, S) ->
- ok = error_logger:format("~p(~p): handle_cast(~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_cast(~tp, ~tp)~n",
[?MODULE, self(), Msg, S]),
{noreply, S}.
@@ -272,10 +272,11 @@ handle_event(#wx{id = Id,
end,
FileName = lists:flatten(["et_contents_viewer_", now_to_string(TimeStamp), ".txt"]),
Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT,
- Msg = "Select a file to the events to",
+ Msg = "Select a file to save events to",
case select_file(S#state.frame, Msg, filename:absname(FileName), Style) of
{ok, FileName2} ->
- Bin = list_to_binary(event_to_string(Event, S#state.event_order)),
+ EventString = event_to_string(Event, S#state.event_order),
+ Bin = unicode:characters_to_binary(EventString),
ok = file:write_file(FileName2, Bin);
cancel ->
ok
@@ -381,7 +382,7 @@ handle_event(#wx{event = #wxSize{size = {W, H}}}, S) ->
S2 = S#state{width = W, height = H},
{noreply, S2};
handle_event(Wx = #wx{}, S) ->
- io:format("~p got an unexpected event: ~p\n", [self(), Wx]),
+ io:format("~p got an unexpected event: ~tp\n", [self(), Wx]),
{noreply, S}.
%%----------------------------------------------------------------------
@@ -405,7 +406,7 @@ handle_info({'EXIT', Pid, Reason}, S) ->
{noreply, S}
end;
handle_info(Info, S) ->
- ok = error_logger:format("~p(~p): handle_info(~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_info(~tp, ~tp)~n",
[?MODULE, self(), Info, S]),
{noreply, S}.
@@ -606,8 +607,8 @@ do_config_editor(Editor, Event, _Colour, TsKey) ->
%%%----------------------------------------------------------------------
term_to_string(Term) ->
- case catch io_lib:format("~s", [Term]) of
- {'EXIT', _} -> io_lib:format("~p", [Term]);
+ case catch io_lib:format("~ts", [Term]) of
+ {'EXIT', _} -> io_lib:format("~tp", [Term]);
GoodString -> GoodString
end.
@@ -659,7 +660,7 @@ pad_string(Int, MinLen, Char, Dir) when is_integer(Int) ->
pad_string(Atom, MinLen, Char, Dir) when is_atom(Atom) ->
pad_string(atom_to_list(Atom), MinLen, Char, Dir);
pad_string(String, MinLen, Char, Dir) when is_integer(MinLen), MinLen >= 0 ->
- Len = length(String),
+ Len = string:length(String),
case {Len >= MinLen, Dir} of
{true, _} ->
String;
diff --git a/lib/et/src/et_wx_viewer.erl b/lib/et/src/et_wx_viewer.erl
index 9613299e6b..4dd44e7a4c 100644
--- a/lib/et/src/et_wx_viewer.erl
+++ b/lib/et/src/et_wx_viewer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -352,7 +352,7 @@ handle_call({open_event, N}, _From, S) when is_integer(N), N > 0->
Reply = do_open_event(S, N),
reply(Reply, S);
handle_call(Request, From, S) ->
- ok = error_logger:format("~p(~p): handle_call(~p, ~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_call(~tp, ~tp, ~tp)~n",
[?MODULE, self(), Request, From, S]),
Reply = {error, {bad_request, Request}},
reply(Reply, S).
@@ -365,7 +365,7 @@ handle_call(Request, From, S) ->
%%----------------------------------------------------------------------
handle_cast(Msg, S) ->
- ok = error_logger:format("~p(~p): handle_cast(~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_cast(~tp, ~tp)~n",
[?MODULE, self(), Msg, S]),
noreply(S).
@@ -803,7 +803,7 @@ handle_info(timeout, S) ->
handle_info({'EXIT', Pid, Reason}, S) ->
if
Pid =:= S#state.collector_pid ->
- io:format("collector died: ~p\n\n", [Reason]),
+ io:format("collector died: ~tp\n\n", [Reason]),
wxFrame:destroy(S#state.frame),
{stop, Reason, S};
Pid =:= S#state.parent_pid ->
@@ -853,10 +853,10 @@ handle_info(#wx{event = #wxPaint{}}, S) ->
S2 = refresh_main_window(S),
noreply(S2);
handle_info(#wx{event = #wxMouse{type = T, x=X,y=Y}}, S) ->
- io:format("~p ~p\n", [T, {X,Y}]),
+ io:format("~tp ~tp\n", [T, {X,Y}]),
noreply(S);
handle_info(Info, S) ->
- ok = error_logger:format("~p(~p): handle_info(~p, ~p)~n",
+ ok = error_logger:format("~p(~p): handle_info(~tp, ~tp)~n",
[?MODULE, self(), Info, S]),
noreply(S).
@@ -1162,7 +1162,7 @@ open_viewer(Scale, FilterName, Actors, S) ->
%% unlink(ViewerPid),
ok;
{error, Reason} ->
- ok = error_logger:format("~p: Failed to start a new window: ~p~n",
+ ok = error_logger:format("~p: Failed to start a new window: ~tp~n",
[?MODULE, Reason])
end.
@@ -1393,7 +1393,7 @@ create_filter_menu(S=#state{filter_menu = {Menu,Data}}, ActiveFilterName, Filter
wxMenu:delete(Menu,I)
catch
_:Reason ->
- io:format("Could not delete item: ~p, because ~p.\n", [I, Reason])
+ io:format("Could not delete item: ~tp, because ~tp.\n", [I, Reason])
end
end,
Data),
@@ -1872,7 +1872,7 @@ create_contents_window(Event, {S, Res}) ->
{ok, Pid} ->
{S, [{ok, Pid} | Res]};
{error, Reason} ->
- ok = error_logger:format("~p(~p): create_contents_window(~p) ->~n ~p~n",
+ ok = error_logger:format("~p(~p): create_contents_window(~tp) ->~n ~tp~n",
[?MODULE, self(), Options, Reason]),
{S, [{error, Reason} | Res]};
Stuff ->
@@ -2069,15 +2069,15 @@ create_actor(Name) ->
#actor{name = Name, string = String, include = false, exclude = false}.
name_to_string(Name) ->
- case catch io_lib:format("~s", [Name]) of
- {'EXIT', _} -> lists:flatten(io_lib:format("~w", [Name]));
+ case catch io_lib:format("~ts", [Name]) of
+ {'EXIT', _} -> lists:flatten(io_lib:format("~tw", [Name]));
GoodString -> lists:flatten(GoodString)
end.
pad_string(Atom, MinLen) when is_atom(Atom) ->
pad_string(atom_to_list(Atom), MinLen);
pad_string(String, MinLen) when is_integer(MinLen), MinLen >= 0 ->
- Len = length(String),
+ Len = string:length(String),
case Len >= MinLen of
true ->
String;
diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl
index aa2cffc66d..e340f50a3c 100644
--- a/lib/eunit/src/eunit_lib.erl
+++ b/lib/eunit/src/eunit_lib.erl
@@ -107,7 +107,7 @@ format_stacktrace(Trace) ->
format_stacktrace(Trace, "in function", "in call from").
format_stacktrace([{M,F,A,L}|Fs], Pre, Pre1) when is_integer(A) ->
- [io_lib:fwrite("~ts ~w:~w/~w~ts\n",
+ [io_lib:fwrite("~ts ~w:~tw/~w~ts\n",
[Pre, M, F, A, format_stacktrace_location(L)])
| format_stacktrace(Fs, Pre1, Pre1)];
format_stacktrace([{M,F,As,L}|Fs], Pre, Pre1) when is_list(As) ->
@@ -121,9 +121,9 @@ format_stacktrace([{M,F,As,L}|Fs], Pre, Pre1) when is_list(As) ->
io_lib:fwrite("~ts ~ts ~ts",
[format_arg(A1),F,format_arg(A2)]);
false ->
- io_lib:fwrite("~w(~ts)", [F,format_arglist(As)])
+ io_lib:fwrite("~tw(~ts)", [F,format_arglist(As)])
end,
- [io_lib:fwrite("~ts ~w:~w/~w~ts\n called as ~ts\n",
+ [io_lib:fwrite("~ts ~w:~tw/~w~ts\n called as ~ts\n",
[Pre,M,F,A,format_stacktrace_location(L),C])
| format_stacktrace(Fs,Pre1,Pre1)];
format_stacktrace([{M,F,As}|Fs], Pre, Pre1) ->
@@ -162,15 +162,15 @@ is_op(_M, _F, _A) ->
format_error({bad_test, Term}) ->
error_msg("bad test descriptor", "~tP", [Term, 15]);
format_error({bad_generator, {{M,F,A}, Term}}) ->
- error_msg(io_lib:format("result from generator ~w:~w/~w is not a test",
+ error_msg(io_lib:format("result from generator ~w:~tw/~w is not a test",
[M,F,A]),
"~tP", [Term, 15]);
format_error({generator_failed, {{M,F,A}, Exception}}) ->
- error_msg(io_lib:format("test generator ~w:~w/~w failed",[M,F,A]),
+ error_msg(io_lib:format("test generator ~w:~tw/~w failed",[M,F,A]),
"~ts", [format_exception(Exception)]);
format_error({no_such_function, {M,F,A}})
when is_atom(M), is_atom(F), is_integer(A) ->
- error_msg(io_lib:format("no such function: ~w:~w/~w", [M,F,A]),
+ error_msg(io_lib:format("no such function: ~w:~tw/~w", [M,F,A]),
"", []);
format_error({module_not_found, M}) ->
error_msg("test module not found", "~tp", [M]);
@@ -185,7 +185,7 @@ format_error({cleanup_failed, Exception}) ->
error_msg("context cleanup failed", "~ts",
[format_exception(Exception)]);
format_error({{bad_instantiator, {{M,F,A}, Term}}, _DummyException}) ->
- error_msg(io_lib:format("result from instantiator ~w:~w/~w is not a test",
+ error_msg(io_lib:format("result from instantiator ~w:~tw/~w is not a test",
[M,F,A]),
"~tP", [Term, 15]);
format_error({instantiation_failed, Exception}) ->
@@ -384,11 +384,9 @@ fun_parent(F) ->
{arity, A} = erlang:fun_info(F, arity),
{M, N, A};
{type, local} ->
- [$-|S] = atom_to_list(N),
- C1 = string:chr(S, $/),
- C2 = string:chr(S, $-),
- {M, list_to_atom(string:sub_string(S, 1, C1 - 1)),
- list_to_integer(string:sub_string(S, C1 + 1, C2 - 1))}
+ [$-|S] = atom_to_list(N),
+ [S2, T] = string:split(S, "/", trailing),
+ {M, list_to_atom(S2), element(1, string:to_integer(T))}
end.
-ifdef(TEST).
diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl
index 77a7cf1fd5..2c9a598628 100644
--- a/lib/eunit/src/eunit_tty.erl
+++ b/lib/eunit/src/eunit_tty.erl
@@ -235,7 +235,7 @@ print_test_error({skipped, Reason}, _) ->
format_skipped({module_not_found, M}) ->
io_lib:fwrite("missing module: ~w", [M]);
format_skipped({no_such_function, {M,F,A}}) ->
- io_lib:fwrite("no such function: ~w:~w/~w", [M,F,A]).
+ io_lib:fwrite("no such function: ~w:~tw/~w", [M,F,A]).
print_test_cancel(Reason) ->
fwrite(format_cancel(Reason)).
diff --git a/lib/hipe/cerl/cerl_cconv.erl b/lib/hipe/cerl/cerl_cconv.erl
index 122e6ef039..2cd0e261d5 100644
--- a/lib/hipe/cerl/cerl_cconv.erl
+++ b/lib/hipe/cerl/cerl_cconv.erl
@@ -258,7 +258,7 @@ bind_module_defs([], Env, S) ->
check_function_name(Name, S) ->
case s__is_function_name(Name, S) of
true ->
- error_msg("multiple definitions of function `~w'.", [Name]),
+ error_msg("multiple definitions of function `~tw'.", [Name]),
exit(error);
false ->
ok
diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl
index 9bf8547745..f07ec52cfa 100644
--- a/lib/kernel/src/error_logger.erl
+++ b/lib/kernel/src/error_logger.erl
@@ -499,27 +499,16 @@ display4(A = [_|_]) ->
display4(A) ->
erlang:display(A).
-string_p([]) ->
+
+string_p(Term) when is_list(Term) ->
+ string_p1(lists:flatten(Term));
+string_p(_Term) ->
+ false.
+
+string_p1([]) ->
false;
-string_p(Term) ->
- string_p1(Term).
-
-string_p1([H|T]) when is_integer(H), H >= $\s, H < 255 ->
- string_p1(T);
-string_p1([$\n|T]) -> string_p1(T);
-string_p1([$\r|T]) -> string_p1(T);
-string_p1([$\t|T]) -> string_p1(T);
-string_p1([$\v|T]) -> string_p1(T);
-string_p1([$\b|T]) -> string_p1(T);
-string_p1([$\f|T]) -> string_p1(T);
-string_p1([$\e|T]) -> string_p1(T);
-string_p1([H|T]) when is_list(H) ->
- case string_p1(H) of
- true -> string_p1(T);
- _ -> false
- end;
-string_p1([]) -> true;
-string_p1(_) -> false.
+string_p1(FlatList) ->
+ io_lib:printable_list(FlatList).
-spec limit_term(term()) -> term().
diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl
index 6504174cbc..6e8f64d932 100644
--- a/lib/kernel/src/file_server.erl
+++ b/lib/kernel/src/file_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -207,7 +207,7 @@ handle_call(stop, _From, Handle) ->
{stop, normal, stopped, Handle};
handle_call(Request, From, Handle) ->
- error_logger:error_msg("handle_call(~p, ~p, _)", [Request, From]),
+ error_logger:error_msg("handle_call(~tp, ~tp, _)", [Request, From]),
{noreply, Handle}.
%%----------------------------------------------------------------------
@@ -220,7 +220,7 @@ handle_call(Request, From, Handle) ->
-spec handle_cast(term(), state()) -> {'noreply', state()}.
handle_cast(Msg, State) ->
- error_logger:error_msg("handle_cast(~p, _)", [Msg]),
+ error_logger:error_msg("handle_cast(~tp, _)", [Msg]),
{noreply, State}.
%%----------------------------------------------------------------------
@@ -243,7 +243,7 @@ handle_info({'EXIT', Handle, _Reason}, Handle) ->
{stop, normal, Handle};
handle_info(Info, State) ->
- error_logger:error_msg("handle_Info(~p, _)", [Info]),
+ error_logger:error_msg("handle_Info(~tp, _)", [Info]),
{noreply, State}.
%%----------------------------------------------------------------------
diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl
index a9e92b28b8..a38522eb5c 100644
--- a/lib/kernel/src/global.erl
+++ b/lib/kernel/src/global.erl
@@ -262,7 +262,7 @@ check_dupname(Name, Pid) ->
{ok, allow} ->
true;
_ ->
- S = "global: ~w registered under several names: ~w\n",
+ S = "global: ~w registered under several names: ~tw\n",
Names = [Name | [Name1 || {_Pid, Name1} <- PidNames]],
error_logger:error_msg(S, [Pid, Names]),
false
@@ -659,7 +659,7 @@ handle_call(stop, _From, S) ->
handle_call(Request, From, S) ->
error_logger:warning_msg("The global_name_server "
"received an unexpected message:\n"
- "handle_call(~p, ~p, _)\n",
+ "handle_call(~tp, ~tp, _)\n",
[Request, From]),
{noreply, S}.
@@ -828,7 +828,7 @@ handle_cast({async_del_lock, _ResourceId, _Pid}, S) ->
handle_cast(Request, S) ->
error_logger:warning_msg("The global_name_server "
"received an unexpected message:\n"
- "handle_cast(~p, _)\n", [Request]),
+ "handle_cast(~tp, _)\n", [Request]),
{noreply, S}.
%%========================================================================
@@ -955,7 +955,7 @@ handle_info({'DOWN', MonitorRef, process, _Pid, _Info}, S0) ->
handle_info(Message, S) ->
error_logger:warning_msg("The global_name_server "
"received an unexpected message:\n"
- "handle_info(~p, _)\n", [Message]),
+ "handle_info(~tp, _)\n", [Message]),
{noreply, S}.
@@ -1949,13 +1949,13 @@ exchange_names([{Name, Pid, Method} | Tail], Node, Ops, Res) ->
exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
{badrpc, Badrpc} ->
error_logger:info_msg("global: badrpc ~w received when "
- "conflicting name ~w was found\n",
+ "conflicting name ~tw was found\n",
[Badrpc, Name]),
Op = {insert, {Name, Pid, Method}},
exchange_names(Tail, Node, [Op | Ops], Res);
Else ->
error_logger:info_msg("global: Resolve method ~w for "
- "conflicting name ~w returned ~w\n",
+ "conflicting name ~tw returned ~tw\n",
[Method, Name, Else]),
Op = {delete, Name},
exchange_names(Tail, Node, [Op | Ops], [Op | Res])
@@ -1984,7 +1984,7 @@ minmax(P1,P2) ->
Pid2 :: pid().
random_exit_name(Name, Pid, Pid2) ->
{Min, Max} = minmax(Pid, Pid2),
- error_logger:info_msg("global: Name conflict terminating ~w\n",
+ error_logger:info_msg("global: Name conflict terminating ~tw\n",
[{Name, Max}]),
exit(Max, kill),
Min.
@@ -2200,7 +2200,7 @@ unexpected_message({'EXIT', _Pid, _Reason}, _What) ->
ok;
unexpected_message(Message, What) ->
error_logger:warning_msg("The global_name_server ~w process "
- "received an unexpected message:\n~p\n",
+ "received an unexpected message:\n~tp\n",
[What, Message]).
%%% Utilities
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index ddda396713..7da89dd7cb 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -778,7 +778,7 @@ handle_info(transition_period_end,
{noreply,State#state{tick = #tick{ticker = Tckr, time = T}}};
handle_info(X, State) ->
- error_msg("Net kernel got ~w~n",[X]),
+ error_msg("Net kernel got ~tw~n",[X]),
{noreply,State}.
%% -----------------------------------------------------------
diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl
index edf4aedde2..c4732f37ee 100644
--- a/lib/kernel/src/pg2.erl
+++ b/lib/kernel/src/pg2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -199,7 +199,7 @@ handle_call({delete, Name}, _From, S) ->
{reply, ok, S};
handle_call(Request, From, S) ->
error_logger:warning_msg("The pg2 server received an unexpected message:\n"
- "handle_call(~p, ~p, _)\n",
+ "handle_call(~tp, ~tp, _)\n",
[Request, From]),
{noreply, S}.
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index b9942e899f..efe3a68531 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -148,8 +148,8 @@ release_tests_spec: make_emakefile
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)"
- $(INSTALL_DATA) kernel.spec kernel_smoke.spec $(EMAKEFILE)\
- $(COVERFILE) "$(RELSYSDIR)"
+ $(INSTALL_DATA) kernel.spec kernel_smoke.spec kernel_bench.spec \
+ $(EMAKEFILE) $(COVERFILE) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
@tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/lib/kernel/test/kernel_bench.spec b/lib/kernel/test/kernel_bench.spec
new file mode 100644
index 0000000000..8de60dae31
--- /dev/null
+++ b/lib/kernel/test/kernel_bench.spec
@@ -0,0 +1 @@
+{groups,"../kernel_test",zlib_SUITE,[bench]}.
diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl
index 4b67fce9a8..e246276262 100644
--- a/lib/kernel/test/zlib_SUITE.erl
+++ b/lib/kernel/test/zlib_SUITE.erl
@@ -21,60 +21,56 @@
-module(zlib_SUITE).
-include_lib("common_test/include/ct.hrl").
-
--compile(export_all).
-
--define(error(Format,Args),
- put(test_server_loc,{?MODULE,?LINE}),
- error(Format,Args,?MODULE,?LINE)).
-
-%% Learn erts team how to really write tests ;-)
--define(m(ExpectedRes,Expr),
- fun() ->
- ACtual1 = (catch (Expr)),
- try case ACtual1 of
- ExpectedRes -> ACtual1
- end
- catch
- error:{case_clause,ACtuAl} ->
- ?error("Not Matching Actual result was:~n ~p ~n",
- [ACtuAl]),
- ACtuAl
- end
- end()).
-
--define(BARG, {'EXIT',{badarg,[{zlib,_,_,_}|_]}}).
--define(DATA_ERROR, {'EXIT',{data_error,[{zlib,_,_,_}|_]}}).
-
-init_per_testcase(_Func, Config) ->
- Config.
-
-end_per_testcase(_Func, _Config) ->
- ok.
-
-error(Format, Args, File, Line) ->
- io:format("~p:~p: ERROR: " ++ Format, [File,Line|Args]),
- group_leader() ! {failed, File, Line}.
-
-%% Hopefully I don't need this to get it to work with the testserver..
-%% Fail = #'REASON'{file = filename:basename(File),
-%% line = Line,
-%% desc = Args},
-%% case global:whereis_name(mnesia_test_case_sup) of
-%% undefined ->
-%% ignore;
-%% Pid ->
-%% Pid ! Fail
-%% %% global:send(mnesia_test_case_sup, Fail),
-%% end,
-%% log("<>ERROR<>~n" ++ Format, Args, File, Line).
+-include_lib("common_test/include/ct_event.hrl").
+
+-export([suite/0, all/0, groups/0]).
+
+%% API group
+-export([api_open_close/1]).
+-export([api_deflateInit/1, api_deflateSetDictionary/1, api_deflateReset/1,
+ api_deflateParams/1, api_deflate/1, api_deflateEnd/1]).
+-export([api_inflateInit/1, api_inflateReset/1, api_inflate2/1, api_inflate3/1,
+ api_inflateChunk/1, api_safeInflate/1, api_inflateEnd/1]).
+-export([api_inflateSetDictionary/1, api_inflateGetDictionary/1]).
+-export([api_crc32/1, api_adler32/1]).
+-export([api_un_compress/1, api_un_zip/1, api_g_un_zip/1]).
+
+%% Examples group
+-export([intro/1]).
+
+%% Usage group
+-export([zip_usage/1, gz_usage/1, gz_usage2/1, compress_usage/1,
+ dictionary_usage/1, large_deflate/1, crc/1, adler/1,
+ only_allow_owner/1, sub_heap_binaries/1]).
+
+%% Bench group
+-export([inflate_bench_zeroed/1, inflate_bench_rand/1,
+ deflate_bench_zeroed/1, deflate_bench_rand/1,
+ chunk_bench_zeroed/1, chunk_bench_rand/1]).
+
+%% Others
+-export([smp/1, otp_9981/1, otp_7359/1]).
+
+-define(m(Guard, Expression),
+ fun() ->
+ Actual = (catch (Expression)),
+ case Actual of
+ Guard -> Actual;
+ _Other ->
+ ct:fail("Failed to match ~p, actual result was ~p",
+ [??Guard, Actual])
+ end
+ end()).
+
+-define(EXIT(Reason), {'EXIT',{Reason,[{_,_,_,_}|_]}}).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,1}}].
all() ->
- [{group, api}, {group, examples}, {group, func}, smp,
+ [{group, api}, {group, examples}, {group, func},
+ {group, bench}, smp,
otp_9981,
otp_7359].
@@ -84,28 +80,19 @@ groups() ->
api_deflateSetDictionary, api_deflateReset,
api_deflateParams, api_deflate, api_deflateEnd,
api_inflateInit, api_inflateSetDictionary, api_inflateGetDictionary,
- api_inflateSync, api_inflateReset, api_inflate, api_inflateChunk,
- api_inflateEnd, api_setBufsz, api_getBufsz, api_crc32,
- api_adler32, api_getQSize, api_un_compress, api_un_zip,
+ api_inflateReset, api_inflate2, api_inflate3, api_inflateChunk,
+ api_safeInflate, api_inflateEnd, api_crc32,
+ api_adler32, api_un_compress, api_un_zip,
api_g_un_zip]},
{examples, [], [intro]},
{func, [],
[zip_usage, gz_usage, gz_usage2, compress_usage,
- dictionary_usage, large_deflate, crc, adler]}].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
+ dictionary_usage, large_deflate, crc, adler,
+ only_allow_owner, sub_heap_binaries]},
+ {bench,
+ [inflate_bench_zeroed, inflate_bench_rand,
+ deflate_bench_zeroed, deflate_bench_rand,
+ chunk_bench_zeroed, chunk_bench_rand]}].
%% Test open/0 and close/1.
api_open_close(Config) when is_list(Config) ->
@@ -113,7 +100,7 @@ api_open_close(Config) when is_list(Config) ->
Fd2 = zlib:open(),
?m(false,Fd1 == Fd2),
?m(ok,zlib:close(Fd1)),
- ?m(?BARG, zlib:close(Fd1)),
+ ?m(?EXIT(not_initialized), zlib:close(Fd1)),
?m(ok,zlib:close(Fd2)),
%% Make sure that we don't get any EXIT messages if trap_exit is enabled.
@@ -128,9 +115,11 @@ api_open_close(Config) when is_list(Config) ->
%% Test deflateInit/2 and /6.
api_deflateInit(Config) when is_list(Config) ->
Z1 = zlib:open(),
- ?m(?BARG, zlib:deflateInit(gurka, none)),
- ?m(?BARG, zlib:deflateInit(gurka, gurka)),
- ?m(?BARG, zlib:deflateInit(Z1, gurka)),
+
+ ?m(?EXIT(badarg), zlib:deflateInit(gurka, none)),
+
+ ?m(?EXIT(bad_compression_level), zlib:deflateInit(gurka, gurka)),
+ ?m(?EXIT(bad_compression_level), zlib:deflateInit(Z1, gurka)),
Levels = [none, default, best_speed, best_compression] ++ lists:seq(0,9),
lists:foreach(fun(Level) ->
Z = zlib:open(),
@@ -138,20 +127,30 @@ api_deflateInit(Config) when is_list(Config) ->
?m(ok,zlib:close(Z))
end, Levels),
%% /6
- ?m(?BARG, zlib:deflateInit(Z1,gurka,deflated,-15,8,default)),
-
- ?m(?BARG, zlib:deflateInit(Z1,default,undefined,-15,8,default)),
-
- ?m(?BARG, zlib:deflateInit(Z1,default,deflated,48,8,default)),
- ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-20,8,default)),
- ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-7,8,default)),
- ?m(?BARG, zlib:deflateInit(Z1,default,deflated,7,8,default)),
-
- ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,0,default)),
- ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,10,default)),
-
- ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,8,0)),
- ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,8,undefined)),
+ ?m(?EXIT(bad_compression_level),
+ zlib:deflateInit(Z1,gurka,deflated,-15,8,default)),
+
+ ?m(?EXIT(bad_compression_method),
+ zlib:deflateInit(Z1,default,undefined,-15,8,default)),
+
+ ?m(?EXIT(bad_compression_strategy),
+ zlib:deflateInit(Z1,default,deflated,-15,8,0)),
+ ?m(?EXIT(bad_compression_strategy),
+ zlib:deflateInit(Z1,default,deflated,-15,8,undefined)),
+
+ ?m(?EXIT(bad_windowbits),
+ zlib:deflateInit(Z1,default,deflated,48,8,default)),
+ ?m(?EXIT(bad_windowbits),
+ zlib:deflateInit(Z1,default,deflated,-20,8,default)),
+ ?m(?EXIT(bad_windowbits),
+ zlib:deflateInit(Z1,default,deflated,-7,8,default)),
+ ?m(?EXIT(bad_windowbits),
+ zlib:deflateInit(Z1,default,deflated,7,8,default)),
+
+ ?m(?EXIT(bad_memlevel),
+ zlib:deflateInit(Z1,default,deflated,-15,0,default)),
+ ?m(?EXIT(bad_memlevel),
+ zlib:deflateInit(Z1,default,deflated,-15,10,default)),
lists:foreach(fun(Level) ->
Z = zlib:open(),
@@ -183,7 +182,11 @@ api_deflateInit(Config) when is_list(Config) ->
?m(ok,zlib:close(Z))
end, Strategies),
?m(ok, zlib:deflateInit(Z1,default,deflated,-15,8,default)),
- ?m({'EXIT',_}, zlib:deflateInit(Z1,none,deflated,-15,8,default)), %% ??
+
+ %% Let it crash for any reason; we don't care about the order in which the
+ %% parameters are checked.
+ ?m(?EXIT(_), zlib:deflateInit(Z1,none,deflated,-15,8,default)),
+
?m(ok, zlib:close(Z1)).
%% Test deflateSetDictionary.
@@ -192,17 +195,17 @@ api_deflateSetDictionary(Config) when is_list(Config) ->
?m(ok, zlib:deflateInit(Z1, default)),
?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, <<1,1,2,3,4,5,1>>)),
?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, [1,1,2,3,4,5,1])),
- ?m(?BARG, zlib:deflateSetDictionary(Z1, gurka)),
- ?m(?BARG, zlib:deflateSetDictionary(Z1, 128)),
- ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)),
- ?m({'EXIT',{stream_error,_}},zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)),
+ ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, gurka)),
+ ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, 128)),
+ ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)),
+ ?m(?EXIT(stream_error), zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)),
?m(ok, zlib:close(Z1)).
%% Test deflateReset.
api_deflateReset(Config) when is_list(Config) ->
Z1 = zlib:open(),
?m(ok, zlib:deflateInit(Z1, default)),
- ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)),
+ ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)),
?m(ok, zlib:deflateReset(Z1)),
?m(ok, zlib:deflateReset(Z1)),
%% FIXME how do I make this go wrong??
@@ -212,9 +215,9 @@ api_deflateReset(Config) when is_list(Config) ->
api_deflateParams(Config) when is_list(Config) ->
Z1 = zlib:open(),
?m(ok, zlib:deflateInit(Z1, default)),
- ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)),
+ ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)),
?m(ok, zlib:deflateParams(Z1, best_compression, huffman_only)),
- ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)),
+ ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)),
?m(ok, zlib:close(Z1)).
%% Test deflate.
@@ -231,11 +234,13 @@ api_deflate(Config) when is_list(Config) ->
?m(B when is_list(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, full)),
?m(B when is_list(B), zlib:deflate(Z1, <<>>, finish)),
- ?m(?BARG, zlib:deflate(gurka, <<1,1,1,1,1,1,1,1,1>>, full)),
- ?m(?BARG, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, asdj)),
- ?m(?BARG, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, 198)),
+ ?m(?EXIT(badarg), zlib:deflate(gurka, <<1,1,1,1,1,1,1,1,1>>, full)),
+
+ ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, asdj)),
+ ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, 198)),
+
%% Causes problems ERROR REPORT
- ?m(?BARG, zlib:deflate(Z1, [asdj,asd], none)),
+ ?m(?EXIT(badarg), zlib:deflate(Z1, [asdj,asd], none)),
?m(ok, zlib:close(Z1)).
@@ -244,11 +249,11 @@ api_deflateEnd(Config) when is_list(Config) ->
Z1 = zlib:open(),
?m(ok, zlib:deflateInit(Z1, default)),
?m(ok, zlib:deflateEnd(Z1)),
- ?m({'EXIT', {einval,_}}, zlib:deflateEnd(Z1)), %% ??
- ?m(?BARG, zlib:deflateEnd(gurka)),
+ ?m(?EXIT(not_initialized), zlib:deflateEnd(Z1)),
+ ?m(?EXIT(badarg), zlib:deflateEnd(gurka)),
?m(ok, zlib:deflateInit(Z1, default)),
?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)),
- ?m({'EXIT', {data_error,_}}, zlib:deflateEnd(Z1)),
+ ?m(?EXIT(data_error), zlib:deflateEnd(Z1)),
?m(ok, zlib:deflateInit(Z1, default)),
?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)),
?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>, finish)),
@@ -259,9 +264,9 @@ api_deflateEnd(Config) when is_list(Config) ->
%% Test inflateInit /1 and /2.
api_inflateInit(Config) when is_list(Config) ->
Z1 = zlib:open(),
- ?m(?BARG, zlib:inflateInit(gurka)),
+ ?m(?EXIT(badarg), zlib:inflateInit(gurka)),
?m(ok, zlib:inflateInit(Z1)),
- ?m({'EXIT',{einval,_}}, zlib:inflateInit(Z1, 15)), %% ??
+ ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 15)),
lists:foreach(fun(Wbits) ->
Z11 = zlib:open(),
?m(ok, zlib:inflateInit(Z11,Wbits)),
@@ -270,33 +275,34 @@ api_inflateInit(Config) when is_list(Config) ->
?m(ok,zlib:close(Z11)),
?m(ok,zlib:close(Z12))
end, lists:seq(8,15)),
- ?m(?BARG, zlib:inflateInit(gurka, -15)),
- ?m(?BARG, zlib:inflateInit(Z1, 7)),
- ?m(?BARG, zlib:inflateInit(Z1, -7)),
- ?m(?BARG, zlib:inflateInit(Z1, 48)),
- ?m(?BARG, zlib:inflateInit(Z1, -16)),
+ ?m(?EXIT(badarg), zlib:inflateInit(gurka, -15)),
+ ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 7)),
+ ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, -7)),
+ ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 48)),
+ ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, -16)),
?m(ok, zlib:close(Z1)).
%% Test inflateSetDictionary.
api_inflateSetDictionary(Config) when is_list(Config) ->
Z1 = zlib:open(),
?m(ok, zlib:inflateInit(Z1)),
- ?m(?BARG, zlib:inflateSetDictionary(gurka,<<1,1,1,1,1>>)),
- ?m(?BARG, zlib:inflateSetDictionary(Z1,102)),
- ?m(?BARG, zlib:inflateSetDictionary(Z1,gurka)),
+ ?m(?EXIT(badarg), zlib:inflateSetDictionary(gurka,<<1,1,1,1,1>>)),
+ ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,102)),
+ ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,gurka)),
Dict = <<1,1,1,1,1>>,
- ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z1,Dict)),
+ ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z1,Dict)),
?m(ok, zlib:close(Z1)).
%% Test inflateGetDictionary.
api_inflateGetDictionary(Config) when is_list(Config) ->
Z1 = zlib:open(),
+ zlib:inflateInit(Z1),
IsOperationSupported =
case catch zlib:inflateGetDictionary(Z1) of
- {'EXIT',{einval,_}} -> true;
- {'EXIT',{enotsup,_}} -> false
+ ?EXIT(not_supported) -> false;
+ _ -> true
end,
- _ = zlib:close(Z1),
+ zlib:close(Z1),
api_inflateGetDictionary_if_supported(IsOperationSupported).
api_inflateGetDictionary_if_supported(false) ->
@@ -306,64 +312,53 @@ api_inflateGetDictionary_if_supported(true) ->
Z1 = zlib:open(),
?m(ok, zlib:deflateInit(Z1)),
Dict = <<"foobar barfoo foo bar far boo">>,
- ?m(_, zlib:deflateSetDictionary(Z1, Dict)),
+ Checksum = zlib:deflateSetDictionary(Z1, Dict),
Payload = <<"foobarbarbar">>,
Compressed = zlib:deflate(Z1, Payload, finish),
?m(ok, zlib:close(Z1)),
- % Decompress and test dictionary extraction
+ % Decompress and test dictionary extraction with inflate/2
Z2 = zlib:open(),
?m(ok, zlib:inflateInit(Z2)),
?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z2))),
- ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z2, Dict)),
- ?m({'EXIT',{{need_dictionary,_},_}}, zlib:inflate(Z2, Compressed)),
+ ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z2, Dict)),
+ ?m(?EXIT({need_dictionary,Checksum}), zlib:inflate(Z2, Compressed)),
?m(ok, zlib:inflateSetDictionary(Z2, Dict)),
?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z2))),
- ?m(Payload, iolist_to_binary(zlib:inflate(Z2, Compressed))),
+ Payload = iolist_to_binary(zlib:inflate(Z2, [])),
?m(ok, zlib:close(Z2)),
- ?m(?BARG, zlib:inflateSetDictionary(Z2, Dict)),
- ok.
+ ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z2, Dict)),
-%% Test inflateSync.
-api_inflateSync(Config) when is_list(Config) ->
- {skip,"inflateSync/1 sucks"}.
-%% Z1 = zlib:open(),
-%% ?m(ok, zlib:deflateInit(Z1)),
-%% B1list0 = zlib:deflate(Z1, "gurkan gurra ger galna tunnor", full),
-%% B2 = zlib:deflate(Z1, "grodan boll", finish),
-%% io:format("~p\n", [B1list0]),
-%% io:format("~p\n", [B2]),
-%% ?m(ok, zlib:deflateEnd(Z1)),
-%% B1 = clobber(14, list_to_binary(B1list0)),
-%% Compressed = list_to_binary([B1,B2]),
-%% io:format("~p\n", [Compressed]),
-
-%% ?m(ok, zlib:inflateInit(Z1)),
-%% ?m(?BARG, zlib:inflateSync(gurka)),
-%% ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, Compressed)),
-%% ?m(ok, zlib:inflateSync(Z1)),
-%% Ubs = zlib:inflate(Z1, []),
-%% <<"grodan boll">> = list_to_binary(Ubs),
-%% ?m(ok, zlib:close(Z1)).
-
-clobber(N, Bin) when is_binary(Bin) ->
- T = list_to_tuple(binary_to_list(Bin)),
- Byte = case element(N, T) of
- 255 -> 254;
- B -> B+1
- end,
- list_to_binary(tuple_to_list(setelement(N, T, Byte))).
+ %% ... And do the same for inflate/3
+ Z3 = zlib:open(),
+ ?m(ok, zlib:inflateInit(Z3)),
+ ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z3))),
+ ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z3, Dict)),
+
+ {need_dictionary, Checksum, _Output = []} =
+ zlib:inflate(Z3, Compressed, [{exception_on_need_dict, false}]),
+
+ ?m(ok, zlib:inflateSetDictionary(Z3, Dict)),
+ ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z3))),
+
+ Payload = iolist_to_binary(
+ zlib:inflate(Z3, [], [{exception_on_need_dict, false}])),
+
+ ?m(ok, zlib:close(Z3)),
+ ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z3, Dict)),
+
+ ok.
%% Test inflateReset.
api_inflateReset(Config) when is_list(Config) ->
Z1 = zlib:open(),
?m(ok, zlib:inflateInit(Z1)),
- ?m(?BARG, zlib:inflateReset(gurka)),
+ ?m(?EXIT(badarg), zlib:inflateReset(gurka)),
?m(ok, zlib:inflateReset(Z1)),
?m(ok, zlib:close(Z1)).
-%% Test inflate.
-api_inflate(Config) when is_list(Config) ->
+%% Test inflate/2
+api_inflate2(Config) when is_list(Config) ->
Data = [<<1,2,2,3,3,3,4,4,4,4>>],
Compressed = zlib:compress(Data),
Z1 = zlib:open(),
@@ -373,12 +368,32 @@ api_inflate(Config) when is_list(Config) ->
?m(ok, zlib:inflateEnd(Z1)),
?m(ok, zlib:inflateInit(Z1)),
?m(Data, zlib:inflate(Z1, Compressed)),
- ?m(?BARG, zlib:inflate(gurka, Compressed)),
- ?m(?BARG, zlib:inflate(Z1, 4384)),
- ?m(?BARG, zlib:inflate(Z1, [atom_list])),
+ ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed)),
+ ?m(?EXIT(badarg), zlib:inflate(Z1, 4384)),
+ ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list])),
?m(ok, zlib:inflateEnd(Z1)),
?m(ok, zlib:inflateInit(Z1)),
- ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, <<2,1,2,1,2>>)),
+ ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>)),
+ ?m(ok, zlib:close(Z1)).
+
+%% Test inflate/3; same as inflate/2 but with the default options inverted.
+api_inflate3(Config) when is_list(Config) ->
+ Data = [<<1,2,2,3,3,3,4,4,4,4>>],
+ Options = [{exception_on_need_dict, false}],
+ Compressed = zlib:compress(Data),
+ Z1 = zlib:open(),
+ ?m(ok, zlib:inflateInit(Z1)),
+ ?m([], zlib:inflate(Z1, <<>>, Options)),
+ ?m(Data, zlib:inflate(Z1, Compressed)),
+ ?m(ok, zlib:inflateEnd(Z1)),
+ ?m(ok, zlib:inflateInit(Z1)),
+ ?m(Data, zlib:inflate(Z1, Compressed, Options)),
+ ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed, Options)),
+ ?m(?EXIT(badarg), zlib:inflate(Z1, 4384, Options)),
+ ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list], Options)),
+ ?m(ok, zlib:inflateEnd(Z1)),
+ ?m(ok, zlib:inflateInit(Z1)),
+ ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>, Options)),
?m(ok, zlib:close(Z1)).
%% Test inflateChunk.
@@ -388,69 +403,105 @@ api_inflateChunk(Config) when is_list(Config) ->
Part1 = binary:part(Data, 0, ChunkSize),
Part2 = binary:part(Data, ChunkSize, ChunkSize),
Part3 = binary:part(Data, ChunkSize * 2, ChunkSize),
+
Compressed = zlib:compress(Data),
Z1 = zlib:open(),
+
zlib:setBufSize(Z1, ChunkSize),
+
?m(ok, zlib:inflateInit(Z1)),
- ?m([], zlib:inflateChunk(Z1, <<>>)),
- ?m({more, Part1}, zlib:inflateChunk(Z1, Compressed)),
- ?m({more, Part2}, zlib:inflateChunk(Z1)),
- ?m(Part3, zlib:inflateChunk(Z1)),
- ?m(ok, zlib:inflateEnd(Z1)),
+ 0 = iolist_size(zlib:inflateChunk(Z1, <<>>)),
+
+ {more, Part1AsIOList} = zlib:inflateChunk(Z1, Compressed),
+ {more, Part2AsIOList} = zlib:inflateChunk(Z1),
+ {more, Part3AsIOList} = zlib:inflateChunk(Z1),
+ [] = zlib:inflateChunk(Z1),
+
+ ?m(Part1, iolist_to_binary(Part1AsIOList)),
+ ?m(Part2, iolist_to_binary(Part2AsIOList)),
+ ?m(Part3, iolist_to_binary(Part3AsIOList)),
+
+ ?m(ok, zlib:inflateEnd(Z1)),
?m(ok, zlib:inflateInit(Z1)),
- ?m({more, Part1}, zlib:inflateChunk(Z1, Compressed)),
+
+ ?m({more, Part1AsIOList}, zlib:inflateChunk(Z1, Compressed)),
?m(ok, zlib:inflateReset(Z1)),
- zlib:setBufSize(Z1, size(Data)),
- ?m(Data, zlib:inflateChunk(Z1, Compressed)),
- ?m(ok, zlib:inflateEnd(Z1)),
+ zlib:setBufSize(Z1, byte_size(Data) + 1),
+
+ DataAsIOList = zlib:inflateChunk(Z1, Compressed),
+ ?m(Data, iolist_to_binary(DataAsIOList)),
+ ?m(ok, zlib:inflateEnd(Z1)),
?m(ok, zlib:inflateInit(Z1)),
- ?m(?BARG, zlib:inflateChunk(gurka, Compressed)),
- ?m(?BARG, zlib:inflateChunk(Z1, 4384)),
- ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)),
+
+ ?m(?EXIT(badarg), zlib:inflateChunk(gurka, Compressed)),
+ ?m(?EXIT(badarg), zlib:inflateChunk(Z1, 4384)),
+
+ ?m(?EXIT(data_error), zlib:inflateEnd(Z1)),
+
?m(ok, zlib:close(Z1)).
-%% Test inflateEnd.
-api_inflateEnd(Config) when is_list(Config) ->
+%% Test safeInflate as a mirror of inflateChunk, but ignore the stuff about
+%% exact chunk sizes.
+api_safeInflate(Config) when is_list(Config) ->
+ Data = << <<(I rem 150)>> || I <- lists:seq(1, 20 bsl 10) >>,
+ Compressed = zlib:compress(Data),
Z1 = zlib:open(),
- ?m({'EXIT',{einval,_}}, zlib:inflateEnd(Z1)),
- ?m(ok, zlib:inflateInit(Z1)),
- ?m(?BARG, zlib:inflateEnd(gurka)),
- ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)),
- ?m({'EXIT',{einval,_}}, zlib:inflateEnd(Z1)),
+
?m(ok, zlib:inflateInit(Z1)),
- ?m(B when is_list(B), zlib:inflate(Z1, zlib:compress("abc"))),
+
+ SafeInflateLoop =
+ fun
+ Loop({continue, Chunk}, Output) ->
+ Loop(zlib:safeInflate(Z1, []), [Output, Chunk]);
+ Loop({finished, Chunk}, Output) ->
+ [Output, Chunk]
+ end,
+
+ Decompressed = SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []),
+ Data = iolist_to_binary(Decompressed),
+
?m(ok, zlib:inflateEnd(Z1)),
- ?m(ok, zlib:close(Z1)).
+ ?m(ok, zlib:inflateInit(Z1)),
-%% Test getBufsz.
-api_getBufsz(Config) when is_list(Config) ->
- Z1 = zlib:open(),
- ?m(Val when is_integer(Val), zlib:getBufSize(Z1)),
- ?m(?BARG, zlib:getBufSize(gurka)),
- ?m(ok, zlib:close(Z1)).
+ {continue, Partial} = zlib:safeInflate(Z1, Compressed),
+ PBin = iolist_to_binary(Partial),
+ PSize = byte_size(PBin),
+ <<PBin:PSize/binary, Rest/binary>> = Data,
-%% Test setBufsz.
-api_setBufsz(Config) when is_list(Config) ->
- Z1 = zlib:open(),
- ?m(?BARG, zlib:setBufSize(Z1, gurka)),
- ?m(?BARG, zlib:setBufSize(gurka, 1232330)),
- Sz = ?m( Val when is_integer(Val), zlib:getBufSize(Z1)),
- ?m(ok, zlib:setBufSize(Z1, Sz*2)),
- DSz = Sz*2,
- ?m(DSz, zlib:getBufSize(Z1)),
+ ?m(ok, zlib:inflateReset(Z1)),
+
+ {continue, Partial} = zlib:safeInflate(Z1, Compressed),
+ PBin = iolist_to_binary(Partial),
+ PSize = byte_size(PBin),
+ <<PBin:PSize/binary, Rest/binary>> = Data,
+
+ ?m(ok, zlib:inflateReset(Z1)),
+
+ SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []),
+
+ ?m(?EXIT(data_error), zlib:safeInflate(Z1, Compressed)),
+
+ ?m(ok, zlib:inflateReset(Z1)),
+ ?m(?EXIT(badarg), zlib:safeInflate(gurka, Compressed)),
+ ?m(?EXIT(badarg), zlib:safeInflate(Z1, 4384)),
+ ?m(?EXIT(data_error), zlib:inflateEnd(Z1)),
?m(ok, zlib:close(Z1)).
-%%% Debug function ??
-%% Test getQSize.
-api_getQSize(Config) when is_list(Config) ->
+%% Test inflateEnd.
+api_inflateEnd(Config) when is_list(Config) ->
Z1 = zlib:open(),
- Q = ?m(Val when is_integer(Val), zlib:getQSize(Z1)),
- io:format("QSize ~p ~n", [Q]),
- ?m(?BARG, zlib:getQSize(gurka)),
+ ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)),
+ ?m(ok, zlib:inflateInit(Z1)),
+ ?m(?EXIT(badarg), zlib:inflateEnd(gurka)),
+ ?m(?EXIT(data_error), zlib:inflateEnd(Z1)),
+ ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)),
+ ?m(ok, zlib:inflateInit(Z1)),
+ ?m(B when is_list(B), zlib:inflate(Z1, zlib:compress("abc"))),
+ ?m(ok, zlib:inflateEnd(Z1)),
?m(ok, zlib:close(Z1)).
%% Test crc32.
@@ -458,8 +509,8 @@ api_crc32(Config) when is_list(Config) ->
Z1 = zlib:open(),
?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)),
Bin = <<1,1,1,1,1,1,1,1,1>>,
- Compressed1 = ?m(_, zlib:deflate(Z1, Bin, none)),
- Compressed2 = ?m(_, zlib:deflate(Z1, <<>>, finish)),
+ Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)),
+ Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)),
Compressed = list_to_binary(Compressed1 ++ Compressed2),
CRC1 = ?m( CRC1 when is_integer(CRC1), zlib:crc32(Z1)),
?m(CRC1 when is_integer(CRC1), zlib:crc32(Z1,Bin)),
@@ -467,15 +518,15 @@ api_crc32(Config) when is_list(Config) ->
?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,Compressed)),
CRC2 = ?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,0,Compressed)),
?m(CRC3 when CRC2 /= CRC3, zlib:crc32(Z1,234,Compressed)),
- ?m(?BARG, zlib:crc32(gurka)),
- ?m(?BARG, zlib:crc32(Z1, not_a_binary)),
- ?m(?BARG, zlib:crc32(gurka, <<1,1,2,4,4>>)),
- ?m(?BARG, zlib:crc32(Z1, 2298929, not_a_binary)),
- ?m(?BARG, zlib:crc32(Z1, not_an_int, <<123,123,123,35,231>>)),
- ?m(?BARG, zlib:crc32_combine(Z1, not_an_int, 123123, 123)),
- ?m(?BARG, zlib:crc32_combine(Z1, noint, 123123, 123)),
- ?m(?BARG, zlib:crc32_combine(Z1, 123123, noint, 123)),
- ?m(?BARG, zlib:crc32_combine(Z1, 123123, 123, noint)),
+ ?m(?EXIT(badarg), zlib:crc32(gurka)),
+ ?m(?EXIT(badarg), zlib:crc32(Z1, not_a_binary)),
+ ?m(?EXIT(badarg), zlib:crc32(gurka, <<1,1,2,4,4>>)),
+ ?m(?EXIT(badarg), zlib:crc32(Z1, 2298929, not_a_binary)),
+ ?m(?EXIT(badarg), zlib:crc32(Z1, not_an_int, <<123,123,123,35,231>>)),
+ ?m(?EXIT(badarg), zlib:crc32_combine(Z1, not_an_int, 123123, 123)),
+ ?m(?EXIT(badarg), zlib:crc32_combine(Z1, noint, 123123, 123)),
+ ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, noint, 123)),
+ ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, 123, noint)),
?m(ok, zlib:deflateEnd(Z1)),
?m(ok, zlib:close(Z1)).
@@ -484,74 +535,115 @@ api_adler32(Config) when is_list(Config) ->
Z1 = zlib:open(),
?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)),
Bin = <<1,1,1,1,1,1,1,1,1>>,
- Compressed1 = ?m(_, zlib:deflate(Z1, Bin, none)),
- Compressed2 = ?m(_, zlib:deflate(Z1, <<>>, finish)),
+ Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)),
+ Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)),
Compressed = list_to_binary(Compressed1 ++ Compressed2),
?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,Bin)),
?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,binary_to_list(Bin))),
ADLER2 = ?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,Compressed)),
?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,1,Compressed)),
?m(ADLER3 when ADLER2 /= ADLER3, zlib:adler32(Z1,234,Compressed)),
- ?m(?BARG, zlib:adler32(Z1, not_a_binary)),
- ?m(?BARG, zlib:adler32(gurka, <<1,1,2,4,4>>)),
- ?m(?BARG, zlib:adler32(Z1, 2298929, not_a_binary)),
- ?m(?BARG, zlib:adler32(Z1, not_an_int, <<123,123,123,35,231>>)),
- ?m(?BARG, zlib:adler32_combine(Z1, noint, 123123, 123)),
- ?m(?BARG, zlib:adler32_combine(Z1, 123123, noint, 123)),
- ?m(?BARG, zlib:adler32_combine(Z1, 123123, 123, noint)),
+ ?m(?EXIT(badarg), zlib:adler32(Z1, not_a_binary)),
+ ?m(?EXIT(badarg), zlib:adler32(gurka, <<1,1,2,4,4>>)),
+ ?m(?EXIT(badarg), zlib:adler32(Z1, 2298929, not_a_binary)),
+ ?m(?EXIT(badarg), zlib:adler32(Z1, not_an_int, <<123,123,123,35,231>>)),
+ ?m(?EXIT(badarg), zlib:adler32_combine(Z1, noint, 123123, 123)),
+ ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, noint, 123)),
+ ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, 123, noint)),
?m(ok, zlib:deflateEnd(Z1)),
?m(ok, zlib:close(Z1)).
%% Test compress.
api_un_compress(Config) when is_list(Config) ->
- ?m(?BARG,zlib:compress(not_a_binary)),
+ ?m(?EXIT(badarg),zlib:compress(not_a_binary)),
Bin = <<1,11,1,23,45>>,
Comp = zlib:compress(Bin),
- ?m(?BARG,zlib:uncompress(not_a_binary)),
- ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<171,171,171,171,171>>)),
- ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<>>)),
- ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120>>)),
- ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156>>)),
- ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156,3>>)),
- ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156,3,0>>)),
- ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<0,156,3,0,0,0,0,1>>)),
+ ?m(?EXIT(badarg),zlib:uncompress(not_a_binary)),
+ ?m(?EXIT(data_error), zlib:uncompress(<<171,171,171,171,171>>)),
+ ?m(?EXIT(data_error), zlib:uncompress(<<>>)),
+ ?m(?EXIT(data_error), zlib:uncompress(<<120>>)),
+ ?m(?EXIT(data_error), zlib:uncompress(<<120,156>>)),
+ ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3>>)),
+ ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3,0>>)),
+ ?m(?EXIT(data_error), zlib:uncompress(<<0,156,3,0,0,0,0,1>>)),
?m(Bin, zlib:uncompress(binary_to_list(Comp))),
?m(Bin, zlib:uncompress(Comp)).
%% Test zip.
api_un_zip(Config) when is_list(Config) ->
- ?m(?BARG,zlib:zip(not_a_binary)),
+ ?m(?EXIT(badarg),zlib:zip(not_a_binary)),
Bin = <<1,11,1,23,45>>,
Comp = zlib:zip(Bin),
?m(Comp, zlib:zip(binary_to_list(Bin))),
- ?m(?BARG,zlib:unzip(not_a_binary)),
- ?m({'EXIT',{data_error,_}}, zlib:unzip(<<171,171,171,171,171>>)),
- ?m({'EXIT',{data_error,_}}, zlib:unzip(<<>>)),
+ ?m(?EXIT(badarg),zlib:unzip(not_a_binary)),
+ ?m(?EXIT(data_error), zlib:unzip(<<171,171,171,171,171>>)),
+ ?m(?EXIT(data_error), zlib:unzip(<<>>)),
?m(Bin, zlib:unzip(Comp)),
?m(Bin, zlib:unzip(binary_to_list(Comp))),
%% OTP-6396
- B = <<131,104,19,100,0,13,99,95,99,105,100,95,99,115,103,115,110,95,50,97,1,107,0,4,208,161,246,29,107,0,3,237,166,224,107,0,6,66,240,153,0,2,10,1,0,8,97,116,116,97,99,104,101,100,104,2,100,0,22,117,112,100,97,116,101,95,112,100,112,95,99,111,110,116,101,120,116,95,114,101,113,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,197,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,5,0,33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,1,114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,103,112,114,115,8,0,104,2,104,2,100,0,8,97,99,116,105,118,97,116,101,104,23,100,0,11,112,100,112,95,99,111,110,116,1,120,116,100,0,7,112,114,105,109,97,114,121,97,1,100,0,9,117,110,100,101,102,105,110,101,100,97,1,97,4,97,4,97,7,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,10100,100,0,9,117,110,100,101,102,105,110,101,100,100,0,5,102,97,108,115,101,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,1,101,100,97,0,100,0,9,117,110,100,101,102,105,110,101,100,107,0,4,16,0,1,144,107,0,4,61,139,186,181,107,0,4,10,8,201,49,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,0,101,100,100,0,9,117,110,100,101,102,105,110,101,100,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,21,106,108,0,0,0,3,104,2,97,1,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,167,20,104,2,97,4,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,21,104,2,97,10,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,26,106,100,0,5,118,101,114,57,57,100,0,9,117,110,0,101,102,105,110,101,100,107,0,2,0,244,107,0,4,10,6,102,195,107,0,4,10,6,102,195,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,107,0,125,248,143,0,203,25115,157,116,65,185,65,172,55,87,164,88,225,50,203,251,115,157,116,65,185,65,172,55,87,164,88,225,50,0,0,82,153,50,0,200,98,87,148,237,193,185,65,149,167,69,144,14,16,153,50,3,81,70,94,13,109,193,1,120,5,181,113,198,118,50,3,81,70,94,13,109,193,185,120,5,181,113,198,118,153,3,81,70,94,13,109,193,185,120,5,181,113,198,118,153,50,16,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,113,92,2,119,128,0,0,108,0,0,1,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,11,97,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,0,33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,101,114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,103,112,114,115,8,0,106>>,
+ B =
+ <<131,104,19,100,0,13,99,95,99,105,100,95,99,115,103,115,110,95,50,97,
+ 1,107,0,4,208,161,246,29,107,0,3,237,166,224,107,0,6,66,240,153,0,2,
+ 10,1,0,8,97,116,116,97,99,104,101,100,104,2,100,0,22,117,112,100,97,
+ 116,101,95,112,100,112,95,99,111,110,116,101,120,116,95,114,101,113,
+ 107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,197,
+ 31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,
+ 64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,5,0,33,4,4,10
+ ,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,1,114,45,97,
+ 112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,
+ 103,112,114,115,8,0,104,2,104,2,100,0,8,97,99,116,105,118,97,116,101,
+ 104,23,100,0,11,112,100,112,95,99,111,110,116,1,120,116,100,0,7,112,
+ 114,105,109,97,114,121,97,1,100,0,9,117,110,100,101,102,105,110,101,
+ 100,97,1,97,4,97,4,97,7,100,0,9,117,110,100,101,102,105,110,101,100,
+ 100,0,9,117,110,100,101,102,105,110,10100,100,0,9,117,110,100,101,
+ 102,105,110,101,100,100,0,5,102,97,108,115,101,100,0,9,117,110,100,
+ 101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,
+ 100,0,9,117,110,100,101,102,105,1,101,100,97,0,100,0,9,117,110,100,
+ 101,102,105,110,101,100,107,0,4,16,0,1,144,107,0,4,61,139,186,181,
+ 107,0,4,10,8,201,49,100,0,9,117,110,100,101,102,105,110,101,100,100,
+ 0,9,117,110,100,101,102,105,0,101,100,100,0,9,117,110,100,101,102,
+ 105,110,101,100,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,
+ 16,97,21,106,108,0,0,0,3,104,2,97,1,104,2,104,3,98,0,0,7,214,97,11,
+ 97,20,104,3,97,17,97,167,20,104,2,97,4,104,2,104,3,98,0,0,7,214,97,
+ 11,97,20,104,3,97,17,97,16,97,21,104,2,97,10,104,2,104,3,98,0,0,7,
+ 214,97,11,97,20,104,3,97,17,97,16,97,26,106,100,0,5,118,101,114,57,
+ 57,100,0,9,117,110,0,101,102,105,110,101,100,107,0,2,0,244,107,0,4,
+ 10,6,102,195,107,0,4,10,6,102,195,100,0,9,117,110,100,101,102,105,
+ 110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,107,0,125,
+ 248,143,0,203,25115,157,116,65,185,65,172,55,87,164,88,225,50,203,
+ 251,115,157,116,65,185,65,172,55,87,164,88,225,50,0,0,82,153,50,0,
+ 200,98,87,148,237,193,185,65,149,167,69,144,14,16,153,50,3,81,70,94,
+ 13,109,193,1,120,5,181,113,198,118,50,3,81,70,94,13,109,193,185,120,
+ 5,181,113,198,118,153,3,81,70,94,13,109,193,185,120,5,181,113,198,
+ 118,153,50,16,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,113,92,2,119,128,0,0,
+ 108,0,0,1,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,
+ 12,3,11,97,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,
+ 104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,0,
+ 33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,101,
+ 114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,
+ 50,52,48,4,103,112,114,115,8,0,106>>,
+
Z = zlib:zip(B),
?m(B, zlib:unzip(Z)).
%% Test gunzip.
api_g_un_zip(Config) when is_list(Config) ->
- ?m(?BARG,zlib:gzip(not_a_binary)),
+ ?m(?EXIT(badarg),zlib:gzip(not_a_binary)),
Bin = <<1,11,1,23,45>>,
Comp = zlib:gzip(Bin),
?m(Comp, zlib:gzip(binary_to_list(Bin))),
- ?m(?BARG, zlib:gunzip(not_a_binary)),
- ?m(?DATA_ERROR, zlib:gunzip(<<171,171,171,171,171>>)),
- ?m(?DATA_ERROR, zlib:gunzip(<<>>)),
+ ?m(?EXIT(badarg), zlib:gunzip(not_a_binary)),
+ ?m(?EXIT(data_error), zlib:gunzip(<<171,171,171,171,171>>)),
+ ?m(?EXIT(data_error), zlib:gunzip(<<>>)),
?m(Bin, zlib:gunzip(Comp)),
?m(Bin, zlib:gunzip(binary_to_list(Comp))),
%% Bad CRC; bad length.
BadCrc = bad_crc_data(),
- ?m({'EXIT',{data_error,_}},(catch zlib:gunzip(BadCrc))),
+ ?m(?EXIT(data_error),(catch zlib:gunzip(BadCrc))),
BadLen = bad_len_data(),
- ?m({'EXIT',{data_error,_}},(catch zlib:gunzip(BadLen))),
+ ?m(?EXIT(data_error),(catch zlib:gunzip(BadLen))),
ok.
bad_crc_data() ->
@@ -594,30 +686,15 @@ intro(Config) when is_list(Config) ->
large_deflate(Config) when is_list(Config) ->
large_deflate_do().
large_deflate_do() ->
- Z = zlib:open(),
- Plain = rand_bytes(zlib:getBufSize(Z)*5),
- ok = zlib:deflateInit(Z),
- _ZlibHeader = zlib:deflate(Z, [], full),
- Deflated = zlib:deflate(Z, Plain, full),
- ?m(ok, zlib:close(Z)),
- ?m(Plain, zlib:unzip(list_to_binary([Deflated, 3, 0]))).
-
-rand_bytes(Sz) ->
- L = <<8,2,3,6,1,2,3,2,3,4,8,7,3,7,2,3,4,7,5,8,9,3>>,
- rand_bytes(erlang:md5(L),Sz).
-
-rand_bytes(Bin, Sz) when byte_size(Bin) >= Sz ->
- <<Res:Sz/binary, _/binary>> = Bin,
- Res;
-rand_bytes(Bin, Sz) ->
- rand_bytes(<<(erlang:md5(Bin))/binary, Bin/binary>>, Sz).
-
+ Plain = gen_determ_rand_bytes(64 bsl 10),
+ Deflated = zlib:zip(Plain),
+ ?m(Plain, zlib:unzip(Deflated)).
%% Test a standard compressed zip file.
zip_usage(Config) when is_list(Config) ->
zip_usage(zip_usage({get_arg,Config}));
zip_usage({get_arg,Config}) ->
- Out = conf(data_dir,Config),
+ Out = get_data_dir(Config),
{ok,ZIP} = file:read_file(filename:join(Out,"zipdoc.zip")),
{ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")),
{run,ZIP,ORIG};
@@ -688,7 +765,7 @@ zip_usage({run,ZIP,ORIG}) ->
gz_usage(Config) when is_list(Config) ->
gz_usage(gz_usage({get_arg,Config}));
gz_usage({get_arg,Config}) ->
- Out = conf(data_dir,Config),
+ Out = get_data_dir(Config),
{ok,GZIP} = file:read_file(filename:join(Out,"zipdoc.1.gz")),
{ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")),
{ok,GZIP2} = file:read_file(filename:join(Out,"zipdoc.txt.gz")),
@@ -709,7 +786,7 @@ gz_usage2(Config) ->
case os:find_executable("gzip") of
Name when is_list(Name) ->
Z = zlib:open(),
- Out = conf(data_dir,Config),
+ Out = get_data_dir(Config),
{ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")),
Compressed = zlib:gzip(ORIG),
GzOutFile = filename:join(Out,"out.gz"),
@@ -737,7 +814,7 @@ gz_usage2(Config) ->
compress_usage(Config) when is_list(Config) ->
compress_usage(compress_usage({get_arg,Config}));
compress_usage({get_arg,Config}) ->
- Out = conf(data_dir,Config),
+ Out = get_data_dir(Config),
{ok,C1} = file:read_file(filename:join(Out,"png-compressed.zlib")),
{run,C1};
compress_usage({run,C1}) ->
@@ -792,7 +869,7 @@ compress_usage({run,C1}) ->
crc(Config) when is_list(Config) ->
crc(crc({get_arg,Config}));
crc({get_arg,Config}) ->
- Out = conf(data_dir,Config),
+ Out = get_data_dir(Config),
{ok,C1} = file:read_file(filename:join(Out,"zipdoc")),
{run,C1};
crc({run,C1}) ->
@@ -821,7 +898,7 @@ crc({run,C1}) ->
adler(Config) when is_list(Config) ->
adler(adler({get_arg,Config}));
adler({get_arg,Config}) ->
- Out = conf(data_dir,Config),
+ Out = get_data_dir(Config),
File1 = filename:join(Out,"zipdoc"),
{ok,C1} = file:read_file(File1),
{run,C1};
@@ -869,10 +946,14 @@ dictionary_usage({run}) ->
%% Now uncompress.
Z2 = zlib:open(),
?m(ok, zlib:inflateInit(Z2)),
- {'EXIT',{{need_dictionary,DictID},_}} = (catch zlib:inflate(Z2, Compressed)),
+
+ ?m(?EXIT({need_dictionary, DictID}), zlib:inflate(Z2, Compressed)),
+
?m(ok, zlib:inflateSetDictionary(Z2, Dict)),
?m(ok, zlib:inflateSetDictionary(Z2, binary_to_list(Dict))),
+
Uncompressed = ?m(B when is_list(B), zlib:inflate(Z2, [])),
+
?m(ok, zlib:inflateEnd(Z2)),
?m(ok, zlib:close(Z2)),
?m(Data, list_to_binary(Uncompressed)).
@@ -882,33 +963,64 @@ split_bin(<<Part:1997/binary,Rest/binary>>, Acc) ->
split_bin(Last,Acc) ->
lists:reverse([Last|Acc]).
+only_allow_owner(Config) when is_list(Config) ->
+ Z = zlib:open(),
-%% Check concurrent access to zlib driver.
-smp(Config) ->
- case erlang:system_info(smp_support) of
- true ->
- NumOfProcs = lists:min([8,erlang:system_info(schedulers)]),
- io:format("smp starting ~p workers\n",[NumOfProcs]),
+ ?m(ok, zlib:inflateInit(Z)),
+ ?m(ok, zlib:inflateReset(Z)),
- %% Tests to run in parallel.
- Funcs = [zip_usage, gz_usage, compress_usage, dictionary_usage,
- crc, adler],
+ {Pid, Ref} = spawn_monitor(
+ fun() ->
+ ?m(?EXIT(not_on_controlling_process), zlib:inflateReset(Z))
+ end),
- %% We get all function arguments here to avoid repeated parallel
- %% file read access.
- FnAList = lists:map(fun(F) -> {F,?MODULE:F({get_arg,Config})}
- end, Funcs),
+ receive
+ {'DOWN', Ref, process, Pid, _Reason} ->
+ ok
+ after 200 ->
+ ct:fail("Spawned worker timed out.")
+ end,
- Pids = [spawn_link(?MODULE, worker, [rand:uniform(9999),
- list_to_tuple(FnAList),
- self()])
- || _ <- lists:seq(1,NumOfProcs)],
- wait_pids(Pids);
+ ?m(ok, zlib:inflateReset(Z)).
- false ->
- {skipped,"No smp support"}
- end.
+sub_heap_binaries(Config) when is_list(Config) ->
+ Compressed = zlib:compress(<<"gurka">>),
+ ConfLen = erlang:length(Config),
+
+ HeapBin = <<ConfLen:8/integer, Compressed/binary>>,
+ <<_:8/integer, SubHeapBin/binary>> = HeapBin,
+
+ ?m(<<"gurka">>, zlib:uncompress(SubHeapBin)),
+ ok.
+%% Check concurrent access to zlib driver.
+smp(Config) ->
+ case erlang:system_info(smp_support) of
+ true ->
+ NumOfProcs = lists:min([8,erlang:system_info(schedulers)]),
+ io:format("smp starting ~p workers\n",[NumOfProcs]),
+
+ %% Tests to run in parallel.
+ Funcs =
+ [zip_usage, gz_usage, compress_usage, dictionary_usage,
+ crc, adler],
+
+ %% We get all function arguments here to avoid repeated parallel
+ %% file read access.
+ UsageArgs =
+ list_to_tuple([{F, ?MODULE:F({get_arg,Config})} || F <- Funcs]),
+ Parent = self(),
+
+ WorkerFun =
+ fun() ->
+ worker(rand:uniform(9999), UsageArgs, Parent)
+ end,
+
+ Pids = [spawn_link(WorkerFun) || _ <- lists:seq(1, NumOfProcs)],
+ wait_pids(Pids);
+ false ->
+ {skipped,"No smp support"}
+ end.
worker(Seed, FnATpl, Parent) ->
io:format("smp worker ~p, seed=~p~n",[self(),Seed]),
@@ -999,43 +1111,98 @@ otp_9981(Config) when is_list(Config) ->
Ports = lists:sort(erlang:ports()),
ok.
+-define(BENCH_SIZE, (16 bsl 20)).
+
+-define(DECOMPRESS_BENCH(Name, What, Data),
+ Name(Config) when is_list(Config) ->
+ Uncompressed = Data,
+ Compressed = zlib:compress(Uncompressed),
+ What(Compressed, byte_size(Uncompressed))).
+
+-define(COMPRESS_BENCH(Name, What, Data),
+ Name(Config) when is_list(Config) ->
+ Compressed = Data,
+ What(Compressed, byte_size(Compressed))).
+
+?DECOMPRESS_BENCH(inflate_bench_zeroed, throughput_bench_inflate,
+ <<0:(8 * ?BENCH_SIZE)>>).
+?DECOMPRESS_BENCH(inflate_bench_rand, throughput_bench_inflate,
+ gen_determ_rand_bytes(?BENCH_SIZE)).
+
+?DECOMPRESS_BENCH(chunk_bench_zeroed, throughput_bench_chunk,
+ <<0:(8 * ?BENCH_SIZE)>>).
+?DECOMPRESS_BENCH(chunk_bench_rand, throughput_bench_chunk,
+ gen_determ_rand_bytes(?BENCH_SIZE)).
+?COMPRESS_BENCH(deflate_bench_zeroed, throughput_bench_deflate,
+ <<0:(8 * ?BENCH_SIZE)>>).
+?COMPRESS_BENCH(deflate_bench_rand, throughput_bench_deflate,
+ gen_determ_rand_bytes(?BENCH_SIZE)).
+
+throughput_bench_inflate(Compressed, Size) ->
+ Z = zlib:open(),
+ zlib:inflateInit(Z),
+
+ submit_throughput_results(Size,
+ fun() ->
+ zlib:inflate(Z, Compressed)
+ end).
+
+throughput_bench_deflate(Uncompressed, Size) ->
+ Z = zlib:open(),
+ zlib:deflateInit(Z),
+
+ submit_throughput_results(Size,
+ fun() ->
+ zlib:deflate(Z, Uncompressed, finish)
+ end).
+
+throughput_bench_chunk(Compressed, Size) ->
+ Z = zlib:open(),
+ zlib:inflateInit(Z),
+
+ ChunkLoop =
+ fun
+ Loop({more, _}) -> Loop(zlib:inflateChunk(Z));
+ Loop(_) -> ok
+ end,
+
+ submit_throughput_results(Size,
+ fun() ->
+ ChunkLoop(zlib:inflateChunk(Z, Compressed))
+ end).
+
+submit_throughput_results(Size, Fun) ->
+ TimeTaken = measure_perf_counter(Fun, millisecond),
+
+ KBPS = trunc((Size bsr 10) / (TimeTaken / 1000)),
+ ct_event:notify(#event{ name = benchmark_data, data = [{value,KBPS}] }),
+ {comment, io_lib:format("~p ms, ~p KBPS", [TimeTaken, KBPS])}.
+
+measure_perf_counter(Fun, Unit) ->
+ Start = os:perf_counter(Unit),
+ Fun(),
+ os:perf_counter(Unit) - Start.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Helps with testing directly %%%%%%%%%%%%%
-conf(What,Config) ->
- try proplists:get_value(What,Config) of
- undefined ->
- "./zlib_SUITE_data";
- Dir ->
- Dir
+get_data_dir(Config) ->
+ try proplists:get_value(data_dir,Config) of
+ undefined ->
+ "./zlib_SUITE_data";
+ Dir ->
+ Dir
catch
- _:_ -> "./zlib_SUITE_data"
+ _:_ -> "./zlib_SUITE_data"
end.
-t() -> t([all]).
-
-t(What) when not is_list(What) ->
- t([What]);
-t(What) ->
- lists:foreach(fun(T) ->
- try ?MODULE:T([])
- catch _E:_R ->
- Line = get(test_server_loc),
- io:format("Failed ~p:~p ~p ~p ~p~n",
- [T,Line,_E,_R, erlang:get_stacktrace()])
- end
- end, expand(What)).
-
-expand(All) ->
- lists:reverse(expand(All,[])).
-expand([H|T], Acc) ->
- case ?MODULE:H(suite) of
- [] -> expand(T,[H|Acc]);
- Cs ->
- R = expand(Cs, Acc),
- expand(T, R)
- end;
-expand([], Acc) -> Acc.
-
+%% Generates a bunch of statistically random bytes using the size as seed.
+gen_determ_rand_bytes(Size) ->
+ gen_determ_rand_bytes(Size, erlang:md5_init(), <<>>).
+gen_determ_rand_bytes(Size, _Context, Acc) when Size =< 0 ->
+ Acc;
+gen_determ_rand_bytes(Size, Context0, Acc) when Size > 0 ->
+ Context = erlang:md5_update(Context0, <<Size/integer>>),
+ Checksum = erlang:md5_final(Context),
+ gen_determ_rand_bytes(Size - 16, Context, <<Acc/binary, Checksum/binary>>).
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index 3b771e8c5b..b68b2de028 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -2283,9 +2283,9 @@ list_index_plugins([{N,M,F} | T] = Ps, Legend) ->
lists:foldl(fun({N1,_,_}, Wa) ->
erlang:max(Wa, length(pp_ix_name(N1)))
end, 0, Ps)),
- io:fwrite(Legend ++ "~-" ++ W ++ "s - ~s:~s~n",
+ io:fwrite(Legend ++ "~-" ++ W ++ "s - ~s:~ts~n",
[pp_ix_name(N), atom_to_list(M), atom_to_list(F)]),
- [io:fwrite(Indent ++ "~-" ++ W ++ "s - ~s:~s~n",
+ [io:fwrite(Indent ++ "~-" ++ W ++ "s - ~s:~ts~n",
[pp_ix_name(N1), atom_to_list(M1), atom_to_list(F1)])
|| {N1,M1,F1} <- T].
diff --git a/lib/mnesia/src/mnesia_bup.erl b/lib/mnesia/src/mnesia_bup.erl
index 3e55deb958..34f16f178b 100644
--- a/lib/mnesia/src/mnesia_bup.erl
+++ b/lib/mnesia/src/mnesia_bup.erl
@@ -920,7 +920,7 @@ create_dat_files([{schema, Tab, TabDef} | Tail], Ext, LocalTabs) ->
ok ->
ok;
{error, Reason} ->
- mnesia_lib:fatal("Cannot rename file ~p -> ~p: ~p~n",
+ mnesia_lib:fatal("Cannot rename file ~tp -> ~tp: ~tp~n",
[TmpFile, DclFile, Reason])
end
end
@@ -1016,7 +1016,7 @@ disc_only_swap_fun(disc_only_copies, Expunge, Open, Close) ->
ok ->
ok;
{error, Reason} ->
- mnesia_lib:fatal("Cannot rename file ~p -> ~p: ~p~n",
+ mnesia_lib:fatal("Cannot rename file ~tp -> ~tp: ~tp~n",
[TmpFile, DatFile, Reason])
end
end;
diff --git a/lib/mnesia/src/mnesia_checkpoint.erl b/lib/mnesia/src/mnesia_checkpoint.erl
index 8659e4622c..2ff77326a9 100644
--- a/lib/mnesia/src/mnesia_checkpoint.erl
+++ b/lib/mnesia/src/mnesia_checkpoint.erl
@@ -683,14 +683,14 @@ retainer_create(_Cp, R, Tab, Name, Ext = {ext, Alias, Mod}) ->
Cs = val({Tab, cstruct}),
Mod:load_table(Alias, T, {retainer, create_table},
mnesia_schema:cs2list(Cs)),
- dbg_out("Checkpoint retainer created ~p ~p~n", [Name, Tab]),
+ dbg_out("Checkpoint retainer created ~p ~tp~n", [Name, Tab]),
R#retainer{store = {Ext, T}, really_retain = true};
retainer_create(_Cp, R, Tab, Name, disc_only_copies) ->
Fname = tab2retainer({Tab, Name}),
file:delete(Fname),
Args = [{file, Fname}, {type, set}, {keypos, 2}, {repair, false}],
{ok, _} = mnesia_lib:dets_sync_open({Tab, Name}, Args),
- dbg_out("Checkpoint retainer created ~p ~p~n", [Name, Tab]),
+ dbg_out("Checkpoint retainer created ~p ~tp~n", [Name, Tab]),
R#retainer{store = {dets, {Tab, Name}}, really_retain = true};
retainer_create(Cp, R, Tab, Name, Storage) ->
T = ?ets_new_table(mnesia_retainer, [set, public, {keypos, 2}]),
@@ -698,7 +698,7 @@ retainer_create(Cp, R, Tab, Name, Storage) ->
ReallyR = R#retainer.really_retain,
ReallyCp = lists:member(Tab, Overriders),
ReallyR2 = prepare_ram_tab(Tab, T, Storage, ReallyR, ReallyCp),
- dbg_out("Checkpoint retainer created ~p ~p~n", [Name, Tab]),
+ dbg_out("Checkpoint retainer created ~p ~tp~n", [Name, Tab]),
R#retainer{store = {ets, T}, really_retain = ReallyR2}.
%% Copy the dumped table into retainer if needed
@@ -849,7 +849,7 @@ retainer_loop(Cp = #checkpoint_args{is_activated=false, name=Name}) ->
retainer_loop(Cp#checkpoint_args{iterators = Iters});
{system, From, Msg} ->
- dbg_out("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
+ dbg_out("~p got {system, ~p, ~tp}~n", [?MODULE, From, Msg]),
sys:handle_system_msg(Msg, From, Cp#checkpoint_args.supervisor,
?MODULE, [], Cp)
end;
@@ -938,11 +938,11 @@ retainer_loop(Cp = #checkpoint_args{name=Name}) ->
retainer_loop(Cp#checkpoint_args{iterators = Iters});
{system, From, Msg} ->
- dbg_out("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
+ dbg_out("~p got {system, ~p, ~tp}~n", [?MODULE, From, Msg]),
sys:handle_system_msg(Msg, From, Cp#checkpoint_args.supervisor,
?MODULE, [], Cp);
Msg ->
- dbg_out("~p got ~p~n", [?MODULE, Msg])
+ dbg_out("~p got ~tp~n", [?MODULE, Msg])
end.
maybe_activate(Cp)
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index 6b93935cb4..77013489b3 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -446,7 +446,7 @@ try_schedule_late_disc_load(Tabs, Reason, MsgTag) ->
[BadNodes]),
try_schedule_late_disc_load(Tabs, Reason, MsgTag);
{aborted, AbortReason} ->
- fatal("Cannot late_load_tables~p: ~p~n",
+ fatal("Cannot late_load_tables ~tp: ~tp~n",
[[Tabs, Reason, MsgTag], AbortReason])
end.
@@ -535,7 +535,7 @@ try_merge_schema(Nodes, Told0, UserFun) ->
end,
try_merge_schema(Nodes, Told, UserFun);
{atomic, {"Cannot get cstructs", Node, Reason}} ->
- dbg_out("Cannot get cstructs, Node ~p ~p~n", [Node, Reason]),
+ dbg_out("Cannot get cstructs, Node ~p ~tp~n", [Node, Reason]),
timer:sleep(300), % Avoid a endless loop look alike
try_merge_schema(Nodes, Told0, UserFun);
{aborted, {shutdown, _}} -> %% One of the nodes is going down
@@ -826,12 +826,12 @@ handle_call({del_other, Who}, _From, State = #state{others=Others0}) ->
{reply, ok, State#state{others=Others}};
handle_call(Msg, _From, State) ->
- error("~p got unexpected call: ~p~n", [?SERVER_NAME, Msg]),
+ error("~p got unexpected call: ~tp~n", [?SERVER_NAME, Msg]),
noreply(State).
late_disc_load(TabsR, Reason, RemoteLoaders, From,
State = #state{loader_queue = LQ, late_loader_queue = LLQ}) ->
- verbose("Intend to load tables: ~p~n", [TabsR]),
+ verbose("Intend to load tables: ~tp~n", [TabsR]),
?eval_debug_fun({?MODULE, late_disc_load},
[{tabs, TabsR},
{reason, Reason},
@@ -1118,7 +1118,7 @@ handle_cast({adopt_orphans, Node, Tabs}, State) ->
noreply(State2);
handle_cast(Msg, State) ->
- error("~p got unexpected cast: ~p~n", [?SERVER_NAME, Msg]),
+ error("~p got unexpected cast: ~tp~n", [?SERVER_NAME, Msg]),
noreply(State).
handle_sync_tabs([Tab | Tabs], From) ->
@@ -1166,7 +1166,7 @@ handle_info(#dumper_done{worker_pid=Pid, worker_res=Res}, State) ->
State3 = opt_start_worker(State2),
noreply(State3);
true ->
- fatal("Dumper failed: ~p~n state: ~p~n", [Res, State]),
+ fatal("Dumper failed: ~p~n state: ~tp~n", [Res, State]),
{stop, fatal, State}
end;
@@ -1249,7 +1249,7 @@ handle_info(#sender_done{worker_pid=Pid, worker_res=Res}, State) ->
true ->
%% No need to send any message to the table receiver
%% since it will soon get a mnesia_down anyway
- fatal("Sender failed: ~p~n state: ~p~n", [Res, State]),
+ fatal("Sender failed: ~p~n state: ~tp~n", [Res, State]),
{stop, fatal, State}
end;
@@ -1257,7 +1257,7 @@ handle_info({'EXIT', Pid, R}, State) when Pid == State#state.supervisor ->
?SAFE(set(mnesia_status, stopping)),
case State#state.dumper_pid of
undefined ->
- dbg_out("~p was ~p~n", [?SERVER_NAME, R]),
+ dbg_out("~p was ~tp~n", [?SERVER_NAME, R]),
{stop, shutdown, State};
_ ->
noreply(State#state{is_stopping = true})
@@ -1266,12 +1266,12 @@ handle_info({'EXIT', Pid, R}, State) when Pid == State#state.supervisor ->
handle_info({'EXIT', Pid, R}, State) when Pid == State#state.dumper_pid ->
case State#state.dumper_queue of
[#schema_commit_lock{}|Workers] -> %% Schema trans crashed or was killed
- dbg_out("WARNING: Dumper ~p exited ~p~n", [Pid, R]),
+ dbg_out("WARNING: Dumper ~p exited ~tp~n", [Pid, R]),
State2 = State#state{dumper_queue = Workers, dumper_pid = undefined},
State3 = opt_start_worker(State2),
noreply(State3);
_Other ->
- fatal("Dumper or schema commit crashed: ~p~n state: ~p~n", [R, State]),
+ fatal("Dumper or schema commit crashed: ~p~n state: ~tp~n", [R, State]),
{stop, fatal, State}
end;
@@ -1280,15 +1280,15 @@ handle_info(Msg = {'EXIT', Pid, R}, State) when R /= wait_for_tables_timeout ->
true ->
%% No need to send any message to the table receiver
%% since it will soon get a mnesia_down anyway
- fatal("Sender crashed: ~p~n state: ~p~n", [{Pid,R}, State]),
+ fatal("Sender crashed: ~p~n state: ~tp~n", [{Pid,R}, State]),
{stop, fatal, State};
false ->
case lists:keymember(Pid, 1, get_loaders(State)) of
true ->
- fatal("Loader crashed: ~p~n state: ~p~n", [R, State]),
+ fatal("Loader crashed: ~p~n state: ~tp~n", [R, State]),
{stop, fatal, State};
false ->
- error("~p got unexpected info: ~p~n", [?SERVER_NAME, Msg]),
+ error("~p got unexpected info: ~tp~n", [?SERVER_NAME, Msg]),
noreply(State)
end
end;
@@ -1308,7 +1308,7 @@ handle_info({'EXIT', Pid, wait_for_tables_timeout}, State) ->
noreply(State);
handle_info(Msg, State) ->
- error("~p got unexpected info: ~p~n", [?SERVER_NAME, Msg]),
+ error("~p got unexpected info: ~tp~n", [?SERVER_NAME, Msg]),
noreply(State).
sync_tab_timeout(Pid, [{{sync_tab, Tab}, Pids} | Tail]) ->
@@ -2054,7 +2054,7 @@ opt_start_sender2([Sender|R], Pids, Kept, LoaderQ) ->
Pid = spawn_link(?MODULE, send_and_reply,[self(), Sender]),
opt_start_sender2(R,[{Pid,Sender}|Pids],Kept,LoaderQ);
true ->
- verbose("Send table failed ~p not active on this node ~n", [Tab]),
+ verbose("Send table failed ~tp not active on this node ~n", [Tab]),
Sender#send_table.receiver_pid ! {copier_done, node()},
opt_start_sender2(R,Pids, Kept, LoaderQ)
end.
@@ -2239,7 +2239,7 @@ disc_load_table(Tab, Reason, ReplyTo) ->
Done#loader_done{is_loaded = false,
reply = Res};
true ->
- fatal("Cannot load table ~p from disc: ~p~n", [Tab, Res])
+ fatal("Cannot load table ~tp from disc: ~tp~n", [Tab, Res])
end.
filter_active(Tab) ->
diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl
index eb02a585a6..f0ed7aef4a 100644
--- a/lib/mnesia/src/mnesia_dumper.erl
+++ b/lib/mnesia/src/mnesia_dumper.erl
@@ -193,7 +193,7 @@ do_perform_dump(Cont, InPlace, InitBy, Regulator, OldVersion) ->
do_perform_dump(C2, InPlace, InitBy, Regulator, Version)
catch _:R when R =/= fatal ->
ST = erlang:get_stacktrace(),
- Reason = {"Transaction log dump error: ~p~n", [{R, ST}]},
+ Reason = {"Transaction log dump error: ~tp~n", [{R, ST}]},
close_files(InPlace, {error, Reason}, InitBy),
exit(Reason)
end;
@@ -329,7 +329,7 @@ perform_update(Tid, SchemaOps, _DumperMode, _UseDir) ->
ST = erlang:get_stacktrace(),
Error = {error, {"Schema update error", {Reason, ST}}},
close_files(InPlace, Error, InitBy),
- fatal("Schema update error ~p ~p", [{Reason,ST}, SchemaOps])
+ fatal("Schema update error ~tp ~tp", [{Reason,ST}, SchemaOps])
end.
insert_ops(_Tid, _Storage, [], _InPlace, _InitBy, _) -> ok;
@@ -1166,7 +1166,7 @@ needs_dump_ets(Tab) ->
DcdF = mnesia_lib:tab2dcd(Tab),
case file:read_file_info(DcdF) of
{error, Reason} ->
- mnesia_lib:dbg_out("File ~p info_error ~p ~n",
+ mnesia_lib:dbg_out("File ~tp info_error ~tp ~n",
[DcdF, Reason]),
true;
{ok, DcdInfo} ->
@@ -1205,7 +1205,7 @@ prepare_open(Tab, UpdateInPlace) ->
Tmp = mnesia_lib:tab2tmp(Tab),
try ok = mnesia_lib:copy_file(Dat, Tmp)
catch error:Error ->
- fatal("Cannot copy dets file ~p to ~p: ~p~n",
+ fatal("Cannot copy dets file ~tp to ~tp: ~tp~n",
[Dat, Tmp, Error])
end,
Tmp
@@ -1441,7 +1441,7 @@ start_regulator() ->
{ok, Pid} ->
Pid;
{error, Reason} ->
- fatal("Failed to start ~n: ~p~n", [N, Reason])
+ fatal("Failed to start ~n: ~tp~n", [N, Reason])
end
end.
diff --git a/lib/mnesia/src/mnesia_event.erl b/lib/mnesia/src/mnesia_event.erl
index b06043bc61..49b3990086 100644
--- a/lib/mnesia/src/mnesia_event.erl
+++ b/lib/mnesia/src/mnesia_event.erl
@@ -103,11 +103,11 @@ handle_any_event({mnesia_system_event, Event}, State) ->
handle_any_event({mnesia_table_event, Event}, State) ->
handle_table_event(Event, State);
handle_any_event(Msg, State) ->
- report_error("~p got unexpected event: ~p~n", [?MODULE, Msg]),
+ report_error("~p got unexpected event: ~tp~n", [?MODULE, Msg]),
{ok, State}.
handle_table_event({Oper, Record, TransId}, State) ->
- report_info("~p performed by ~p on record:~n\t~p~n",
+ report_info("~p performed by ~p on record:~n\t~tp~n",
[Oper, TransId, Record]),
{ok, State}.
@@ -155,7 +155,7 @@ handle_system_event({mnesia_down, Node}, State) ->
end;
handle_system_event({mnesia_overload, Details}, State) ->
- report_warning("Mnesia is overloaded: ~w~n", [Details]),
+ report_warning("Mnesia is overloaded: ~tw~n", [Details]),
{ok, State};
handle_system_event({mnesia_info, Format, Args}, State) ->
@@ -175,16 +175,16 @@ handle_system_event({mnesia_fatal, Format, Args, BinaryCore}, State) ->
{ok, State#state{dumped_core = true}};
handle_system_event({inconsistent_database, Reason, Node}, State) ->
- report_error("mnesia_event got {inconsistent_database, ~w, ~w}~n",
+ report_error("mnesia_event got {inconsistent_database, ~tw, ~w}~n",
[Reason, Node]),
{ok, State};
handle_system_event({mnesia_user, Event}, State) ->
- report_info("User event: ~p~n", [Event]),
+ report_info("User event: ~tp~n", [Event]),
{ok, State};
handle_system_event(Msg, State) ->
- report_error("mnesia_event got unexpected system event: ~p~n", [Msg]),
+ report_error("mnesia_event got unexpected system event: ~tp~n", [Msg]),
{ok, State}.
report_info(Format0, Args0) ->
diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl
index c79f790973..d121bd01e9 100644
--- a/lib/mnesia/src/mnesia_index.erl
+++ b/lib/mnesia/src/mnesia_index.erl
@@ -420,7 +420,7 @@ make_ram_index(Tab, Storage, [Pos | Tail]) ->
add_ram_index(Tab, Storage, {Pos, _Pref}) ->
Type = ordered,
- verbose("Creating index for ~w ~p ~p~n", [Tab, Pos, Type]),
+ verbose("Creating index for ~tw ~p ~p~n", [Tab, Pos, Type]),
SetOrBag = val({Tab, setorbag}),
IxValsF = index_vals_f(Storage, Tab, Pos),
IxFun = fun(Val, Key) -> {{Val, Key}} end,
diff --git a/lib/mnesia/src/mnesia_late_loader.erl b/lib/mnesia/src/mnesia_late_loader.erl
index e273329ffc..e4f8dcf2b9 100644
--- a/lib/mnesia/src/mnesia_late_loader.erl
+++ b/lib/mnesia/src/mnesia_late_loader.erl
@@ -87,13 +87,13 @@ loop(State) ->
loop(State);
{system, From, Msg} ->
- mnesia_lib:dbg_out("~p got {system, ~p, ~p}~n",
+ mnesia_lib:dbg_out("~p got {system, ~p, ~tp}~n",
[?SERVER_NAME, From, Msg]),
Parent = State#state.supervisor,
sys:handle_system_msg(Msg, From, Parent, ?MODULE, [], State);
Msg ->
- mnesia_lib:error("~p got unexpected message: ~p~n",
+ mnesia_lib:error("~p got unexpected message: ~tp~n",
[?SERVER_NAME, Msg]),
loop(State)
end.
diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl
index 1fdc656600..53fdd76de8 100644
--- a/lib/mnesia/src/mnesia_lib.erl
+++ b/lib/mnesia/src/mnesia_lib.erl
@@ -467,7 +467,7 @@ pr_other(Var) ->
no -> {node_not_running, node()};
_ -> {no_exists, Var}
end,
- verbose("~p (~p) val(mnesia_gvar, ~w) -> ~p ~p ~n",
+ verbose("~p (~tp) val(mnesia_gvar, ~tw) -> ~p ~tp ~n",
[self(), process_info(self(), registered_name),
Var, Why, erlang:get_stacktrace()]),
mnesia:abort(Why).
@@ -654,7 +654,7 @@ coredump() ->
coredump(CrashInfo) ->
Core = mkcore(CrashInfo),
Out = core_file(),
- important("Writing Mnesia core to file: ~p...~p~n", [Out, CrashInfo]),
+ important("Writing Mnesia core to file: ~tp...~tp~n", [Out, CrashInfo]),
_ = file:write_file(Out, Core),
Out.
@@ -844,7 +844,7 @@ vcore() ->
case file:list_dir(Cwd) of
{ok, Files}->
CoreFiles = lists:sort(lists:zf(Filter, Files)),
- show("Mnesia core files: ~p~n", [CoreFiles]),
+ show("Mnesia core files: ~tp~n", [CoreFiles]),
vcore(lists:last(CoreFiles));
Error ->
Error
@@ -853,17 +853,17 @@ vcore() ->
vcore(Bin) when is_binary(Bin) ->
Core = binary_to_term(Bin),
Fun = fun({Item, Info}) ->
- show("***** ~p *****~n", [Item]),
+ show("***** ~tp *****~n", [Item]),
case catch vcore_elem({Item, Info}) of
{'EXIT', Reason} ->
- show("{'EXIT', ~p}~n", [Reason]);
+ show("{'EXIT', ~tp}~n", [Reason]);
_ -> ok
end
end,
lists:foreach(Fun, Core);
vcore(File) ->
- show("~n***** Mnesia core: ~p *****~n", [File]),
+ show("~n***** Mnesia core: ~tp *****~n", [File]),
case file:read_file(File) of
{ok, Bin} ->
vcore(Bin);
@@ -879,7 +879,7 @@ vcore_elem({schema_file, {ok, B}}) ->
vcore_elem({logfile, {ok, BinList}}) ->
Fun = fun({F, Info}) ->
- show("----- logfile: ~p -----~n", [F]),
+ show("----- logfile: ~tp -----~n", [F]),
case Info of
{ok, B} ->
Fname = "/tmp/mnesia_vcore_elem.TMP",
@@ -887,7 +887,7 @@ vcore_elem({logfile, {ok, BinList}}) ->
mnesia_log:view(Fname),
file:delete(Fname);
_ ->
- show("~p~n", [Info])
+ show("~tp~n", [Info])
end
end,
lists:foreach(Fun, BinList);
@@ -895,12 +895,12 @@ vcore_elem({logfile, {ok, BinList}}) ->
vcore_elem({crashinfo, {Format, Args}}) ->
show(Format, Args);
vcore_elem({gvar, L}) ->
- show("~p~n", [lists:sort(L)]);
+ show("~tp~n", [lists:sort(L)]);
vcore_elem({transactions, Info}) ->
mnesia_tm:display_info(user, Info);
vcore_elem({_Item, Info}) ->
- show("~p~n", [Info]).
+ show("~tp~n", [Info]).
fix_error(X) ->
set(last_error, X), %% for debugabililty
@@ -1018,7 +1018,7 @@ report_system_event({'EXIT', Reason}, Event) ->
end;
Error ->
- Msg = "Mnesia(~p): Cannot report event ~p: ~p (~p)~n",
+ Msg = "Mnesia(~tp): Cannot report event ~tp: ~tp (~tp)~n",
error_logger:format(Msg, [node(), Event, Reason, Error])
end,
ok;
diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl
index c710470a2c..4c6336cb73 100644
--- a/lib/mnesia/src/mnesia_loader.erl
+++ b/lib/mnesia/src/mnesia_loader.erl
@@ -46,7 +46,7 @@ val(Var) ->
disc_load_table(Tab, Reason) ->
Storage = val({Tab, storage_type}),
Type = val({Tab, setorbag}),
- dbg_out("Getting table ~p (~p) from disc: ~p~n",
+ dbg_out("Getting table ~tp (~p) from disc: ~tp~n",
[Tab, Storage, Reason]),
?eval_debug_fun({?MODULE, do_get_disc_copy},
[{tab, Tab},
@@ -56,7 +56,7 @@ disc_load_table(Tab, Reason) ->
do_get_disc_copy2(Tab, Reason, Storage, Type).
do_get_disc_copy2(Tab, _Reason, Storage, _Type) when Storage == unknown ->
- verbose("Local table copy of ~p has recently been deleted, ignored.~n",
+ verbose("Local table copy of ~tp has recently been deleted, ignored.~n",
[Tab]),
{not_loaded, storage_unknown};
do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_copies ->
@@ -199,20 +199,20 @@ net_load_table(Tab, Reason, Ns, _Cs) ->
try_net_load_table(Tab, Reason, Ns, val({Tab, cstruct})).
try_net_load_table(Tab, _Reason, [], _Cs) ->
- verbose("Copy failed. No active replicas of ~p are available.~n", [Tab]),
+ verbose("Copy failed. No active replicas of ~tp are available.~n", [Tab]),
{not_loaded, none_active};
try_net_load_table(Tab, Reason, Ns, Cs) ->
Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
do_get_network_copy(Tab, Reason, Ns, Storage, Cs).
do_get_network_copy(Tab, _Reason, _Ns, unknown, _Cs) ->
- verbose("Local table copy of ~p has recently been deleted, ignored.~n", [Tab]),
+ verbose("Local table copy of ~tp has recently been deleted, ignored.~n", [Tab]),
{not_loaded, storage_unknown};
do_get_network_copy(Tab, Reason, Ns, Storage, Cs) ->
[Node | Tail] = Ns,
case lists:member(Node,val({current, db_nodes})) of
true ->
- dbg_out("Getting table ~p (~p) from node ~p: ~p~n",
+ dbg_out("Getting table ~tp (~p) from node ~p: ~tp~n",
[Tab, Storage, Node, Reason]),
?eval_debug_fun({?MODULE, do_get_network_copy},
[{tab, Tab}, {reason, Reason},
@@ -222,7 +222,7 @@ do_get_network_copy(Tab, Reason, Ns, Storage, Cs) ->
set({Tab, load_node}, Node),
set({Tab, load_reason}, Reason),
mnesia_controller:i_have_tab(Tab),
- dbg_out("Table ~p copied from ~p to ~p~n", [Tab, Node, node()]),
+ dbg_out("Table ~tp copied from ~p to ~p~n", [Tab, Node, node()]),
{loaded, ok};
Err = {error, _} when element(1, Reason) == dumper ->
{not_loaded,Err};
@@ -286,12 +286,12 @@ init_receiver(Node, Tab,Storage,Cs,Reason) ->
element(1,Reason) == dumper ->
{error,Result};
{atomic, {error,Result}} ->
- fatal("Cannot create table ~p: ~p~n",
+ fatal("Cannot create table ~tp: ~tp~n",
[[Tab, Storage], Result]);
{atomic, Result} -> Result;
{aborted, nomore} -> restart;
{aborted, _Reas} ->
- verbose("Receiver failed on ~p from ~p:~nReason: ~p~n",
+ verbose("Receiver failed on ~tp from ~p:~nReason: ~tp~n",
[Tab,Node,_Reas]),
down %% either this node or sender is dying
end,
@@ -313,7 +313,7 @@ start_remote_sender(Node,Tab,Storage) ->
{SenderPid, TabSize, DetsData};
%% Protocol conversion hack
{copier_done, Node} ->
- verbose("Sender of table ~p crashed on node ~p ~n", [Tab, Node]),
+ verbose("Sender of table ~tp crashed on node ~p ~n", [Tab, Node]),
down(Tab, Storage)
end.
@@ -374,7 +374,7 @@ do_init_table(Tab,Storage,Cs,SenderPid,
tab_receiver(Node,Tab,Storage,Cs,OrigTabRec);
Reason ->
Msg = "[d]ets:init table failed",
- verbose("~s: ~p: ~p~n", [Msg, Tab, Reason]),
+ verbose("~ts: ~tp: ~tp~n", [Msg, Tab, Reason]),
down(Tab, Storage)
end;
Error ->
@@ -432,7 +432,7 @@ tab_receiver(Node, Tab, Storage, Cs, OrigTabRec) ->
%% Protocol conversion hack
{copier_done, Node} ->
- verbose("Sender of table ~p crashed on node ~p ~n", [Tab, Node]),
+ verbose("Sender of table ~tp crashed on node ~p ~n", [Tab, Node]),
down(Tab, Storage);
{'EXIT', Pid, Reason} ->
@@ -490,7 +490,7 @@ ext_load_table(Mod, Alias, Tab, Reason) ->
ext_init_table(Action, Alias, Mod, Tab, Fun, State, Sender) ->
case Fun(Action) of
{copier_done, Node} ->
- verbose("Receiver of table ~p crashed on ~p (more)~n", [Tab, Node]),
+ verbose("Receiver of table ~tp crashed on ~p (more)~n", [Tab, Node]),
down(Tab, {ext,Alias,Mod});
{Data, NewFun} ->
case Mod:receive_data(Data, Alias, Tab, Sender, State) of
@@ -553,7 +553,7 @@ finish_copy(Storage,Tab,Cs,SenderPid,DatBin,OrigTabRec) ->
ok;
{error, Reason} ->
Msg = "Failed to handle last",
- verbose("~s: ~p: ~p~n", [Msg, Tab, Reason]),
+ verbose("~ts: ~tp: ~tp~n", [Msg, Tab, Reason]),
down(Tab, Storage)
end.
@@ -859,7 +859,7 @@ send_more(Pid, N, Chunk, DataState, Tab, Storage) ->
send_more(Pid, 1, NewChunk, Init(), Tab, Storage);
{copier_done, Node} when Node == node(Pid)->
- verbose("Receiver of table ~p crashed on ~p (more)~n", [Tab, Node]),
+ verbose("Receiver of table ~tp crashed on ~p (more)~n", [Tab, Node]),
throw(receiver_died)
end.
@@ -937,7 +937,7 @@ finish_copy(Pid, Tab, Storage, RemoteS, NeedLock) ->
{Pid, no_more} -> % Dont bother about the spurious 'more' message
no_more;
{copier_done, Node} ->
- verbose("Tab receiver ~p crashed (more): ~p~n", [Tab, Node]),
+ verbose("Tab receiver ~tp crashed (more): ~p~n", [Tab, Node]),
receiver_died
end
end,
diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl
index 59fd89059f..073b48abc0 100644
--- a/lib/mnesia/src/mnesia_locker.erl
+++ b/lib/mnesia/src/mnesia_locker.erl
@@ -245,7 +245,7 @@ loop(State) ->
do_stop();
{system, From, Msg} ->
- verbose("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
+ verbose("~p got {system, ~p, ~tp}~n", [?MODULE, From, Msg]),
Parent = State#state.supervisor,
sys:handle_system_msg(Msg, From, Parent, ?MODULE, [], State);
@@ -254,7 +254,7 @@ loop(State) ->
loop(State);
Msg ->
- error("~p got unexpected message: ~p~n", [?MODULE, Msg]),
+ error("~p got unexpected message: ~tp~n", [?MODULE, Msg]),
loop(State)
end.
diff --git a/lib/mnesia/src/mnesia_log.erl b/lib/mnesia/src/mnesia_log.erl
index 9536effd42..55b1d6e419 100644
--- a/lib/mnesia/src/mnesia_log.erl
+++ b/lib/mnesia/src/mnesia_log.erl
@@ -310,7 +310,7 @@ verify_no_exists(Fname) ->
false ->
ok;
true ->
- fatal("Log file exists: ~p~n", [Fname])
+ fatal("Log file exists: ~tp~n", [Fname])
end.
open_log(Name, Header, Fname) ->
@@ -331,7 +331,7 @@ open_log(Name, Header, Fname, Exists, Repair) ->
open_log(Name, Header, Fname, Exists, Repair, Mode) ->
Args = [{file, Fname}, {name, Name}, {repair, Repair}, {mode, Mode}],
-%% io:format("~p:open_log: ~p ~p~n", [?MODULE, Name, Fname]),
+%% io:format("~p:open_log: ~tp ~tp~n", [?MODULE, Name, Fname]),
case mnesia_monitor:open_log(Args) of
{ok, Log} when Exists == true ->
Log;
@@ -344,19 +344,19 @@ open_log(Name, Header, Fname, Exists, Repair, Mode) ->
write_header(Log, Header),
Log;
{repaired, Log, _Recover, BadBytes} ->
- mnesia_lib:important("Data may be missing, log ~p repaired: Lost ~p bytes~n",
+ mnesia_lib:important("Data may be missing, log ~tp repaired: Lost ~p bytes~n",
[Fname, BadBytes]),
Log;
{error, Reason = {file_error, _Fname, emfile}} ->
- fatal("Cannot open log file ~p: ~p~n", [Fname, Reason]);
+ fatal("Cannot open log file ~tp: ~tp~n", [Fname, Reason]);
{error, Reason} when Repair == true ->
file:delete(Fname),
- mnesia_lib:important("Data may be missing, Corrupt logfile deleted: ~p, ~p ~n",
+ mnesia_lib:important("Data may be missing, Corrupt logfile deleted: ~tp, ~tp ~n",
[Fname, Reason]),
%% Create a new
open_log(Name, Header, Fname, false, false, read_write);
{error, Reason} ->
- fatal("Cannot open log file ~p: ~p~n", [Fname, Reason])
+ fatal("Cannot open log file ~tp: ~tp~n", [Fname, Reason])
end.
write_header(Log, Header) ->
@@ -381,7 +381,7 @@ close_log(Log) ->
{error, {read_only_mode, Log}} ->
ok;
{error, Reason} ->
- mnesia_lib:important("Failed syncing ~p to_disk reason ~p ~n",
+ mnesia_lib:important("Failed syncing ~tp to_disk reason ~tp ~n",
[Log, Reason])
end,
mnesia_monitor:close_log(Log).
@@ -464,13 +464,13 @@ chunk_log(_Log, eof) ->
chunk_log(Log, Cont) ->
case disk_log:chunk(Log, Cont) of
{error, Reason} ->
- fatal("Possibly truncated ~p file: ~p~n",
+ fatal("Possibly truncated ~tp file: ~tp~n",
[Log, Reason]);
{C2, Chunk, _BadBytes} ->
%% Read_only case, should we warn about the bad log file?
%% BUGBUG Should we crash if Repair == false ??
%% We got to check this !!
- mnesia_lib:important("~p repaired, lost ~p bad bytes~n", [Log, _BadBytes]),
+ mnesia_lib:important("~tp repaired, lost ~p bad bytes~n", [Log, _BadBytes]),
{C2, Chunk};
Other ->
Other
@@ -505,7 +505,7 @@ prepare_decision_log_dump(false, Prev) ->
ok ->
prepare_decision_log_dump(true, Prev);
{error, Reason} ->
- fatal("Cannot rename decision log file ~p -> ~p: ~p~n",
+ fatal("Cannot rename decision log file ~tp -> ~tp: ~tp~n",
[decision_log_file(), Prev, Reason])
end;
prepare_decision_log_dump(true, Prev) ->
@@ -522,7 +522,7 @@ confirm_decision_log_dump() ->
ok ->
file:delete(previous_decision_log_file());
{error, Reason} ->
- fatal("Cannot confirm decision log dump: ~p~n",
+ fatal("Cannot confirm decision log dump: ~tp~n",
[Reason])
end.
@@ -561,7 +561,7 @@ view() ->
lists:foreach(fun(F) -> view(F) end, log_files()).
view(File) ->
- mnesia_lib:show("***** ~p ***** ~n", [File]),
+ mnesia_lib:show("***** ~tp ***** ~n", [File]),
case exists(File) of
false ->
nolog;
@@ -574,25 +574,25 @@ view(File) ->
{repaired, _, _, _} ->
view_file(start, N);
{error, Reason} ->
- error("Cannot open log ~p: ~p~n", [File, Reason])
+ error("Cannot open log ~tp: ~tp~n", [File, Reason])
end
end.
view_file(C, Log) ->
case disk_log:chunk(Log, C) of
{error, Reason} ->
- error("** Possibly truncated FILE ~p~n", [Reason]),
+ error("** Possibly truncated FILE ~tp~n", [Reason]),
error;
eof ->
disk_log:close(Log),
eof;
{C2, Terms, _BadBytes} ->
- dbg_out("Lost ~p bytes in ~p ~n", [_BadBytes, Log]),
- lists:foreach(fun(X) -> mnesia_lib:show("~p~n", [X]) end,
+ dbg_out("Lost ~p bytes in ~tp ~n", [_BadBytes, Log]),
+ lists:foreach(fun(X) -> mnesia_lib:show("~tp~n", [X]) end,
Terms),
view_file(C2, Log);
{C2, Terms} ->
- lists:foreach(fun(X) -> mnesia_lib:show("~p~n", [X]) end,
+ lists:foreach(fun(X) -> mnesia_lib:show("~tp~n", [X]) end,
Terms),
view_file(C2, Log)
end.
@@ -750,12 +750,12 @@ abort_write_fun(B, What, Args) ->
abort_write(B, What, Args, Reason) ->
Mod = B#backup_args.module,
Opaque = B#backup_args.opaque,
- dbg_out("Failed to perform backup. M=~p:F=~p:A=~p -> ~p~n",
+ dbg_out("Failed to perform backup. M=~p:F=~tp:A=~tp -> ~tp~n",
[Mod, What, Args, Reason]),
try apply(Mod, abort_write, [Opaque]) of
{ok, _Res} -> throw({error, Reason})
catch _:Other ->
- error("Failed to abort backup. ~p:~p~p -> ~p~n",
+ error("Failed to abort backup. ~p:~tp~tp -> ~tp~n",
[Mod, abort_write, [Opaque], Other]),
throw({error, Reason})
end.
@@ -802,7 +802,7 @@ select_source(Tab, Name, PrevName) ->
{PrevName, retainer};
_ ->
%% Do a full backup anyway
- dbg_out("Incremental backup escalated to full backup: ~p~n", [Tab]),
+ dbg_out("Incremental backup escalated to full backup: ~tp~n", [Tab]),
{Name, table}
end
end.
diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl
index 22a24b6dc9..4cfe16dec0 100644
--- a/lib/mnesia/src/mnesia_monitor.erl
+++ b/lib/mnesia/src/mnesia_monitor.erl
@@ -178,10 +178,10 @@ check_protocol([{Node, {reject, _Mon, Version, Protocol}} | Tail], Protocols) ->
[Node, Protocols, Version, Protocol]),
check_protocol(Tail, Protocols);
check_protocol([{error, _Reason} | Tail], Protocols) ->
- dbg_out("~p connect failed error: ~p~n", [?MODULE, _Reason]),
+ dbg_out("~p connect failed error: ~tp~n", [?MODULE, _Reason]),
check_protocol(Tail, Protocols);
check_protocol([{badrpc, _Reason} | Tail], Protocols) ->
- dbg_out("~p connect failed badrpc: ~p~n", [?MODULE, _Reason]),
+ dbg_out("~p connect failed badrpc: ~tp~n", [?MODULE, _Reason]),
check_protocol(Tail, Protocols);
check_protocol([], [Protocol | _Protocols]) ->
set(protocol_version, Protocol),
@@ -246,10 +246,10 @@ start_proc(Who, Mod, Fun, Args) ->
proc_lib:start_link(mnesia_sp, init_proc, Args2, infinity).
terminate_proc(Who, R, State) when R /= shutdown, R /= killed ->
- fatal("~p crashed: ~p state: ~p~n", [Who, R, State]);
+ fatal("~p crashed: ~p state: ~tp~n", [Who, R, State]);
terminate_proc(Who, Reason, _State) ->
- mnesia_lib:verbose("~p terminated: ~p~n", [Who, Reason]),
+ mnesia_lib:verbose("~p terminated: ~tp~n", [Who, Reason]),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -294,7 +294,7 @@ init([Parent]) ->
{ok, #state{supervisor = Parent}}
catch _:Reason ->
- mnesia_lib:report_fatal("Bad configuration: ~p~n", [Reason]),
+ mnesia_lib:report_fatal("Bad configuration: ~tp~n", [Reason]),
{stop, {bad_config, Reason}}
end.
@@ -333,7 +333,7 @@ handle_call({mktab, Tab, Args}, _From, State) ->
catch error:ExitReason ->
Msg = "Cannot create ets table",
Reason = {system_limit, Msg, Tab, Args, ExitReason},
- fatal("~p~n", [Reason]),
+ fatal("~tp~n", [Reason]),
{noreply, State}
end;
@@ -353,7 +353,7 @@ handle_call({open_dets, Tab, Args}, _From, State) ->
{error, Reason} ->
Msg = "Cannot open dets table",
Error = {error, {Msg, Tab, Args, Reason}},
- fatal("~p~n", [Error]),
+ fatal("~tp~n", [Error]),
{noreply, State}
end;
@@ -385,7 +385,7 @@ handle_call({reopen_log, Name, Fname, Head}, _From, State) ->
{error, Reason} ->
Msg = "Cannot rename disk_log file",
Error = {error, {Msg, Name, Fname, Head, Reason}},
- fatal("~p~n", [Error]),
+ fatal("~tp~n", [Error]),
{noreply, State}
end;
@@ -400,7 +400,7 @@ handle_call({close_log, Name}, _From, State) ->
{error, Reason} ->
Msg = "Cannot close disk_log file",
Error = {error, {Msg, Name, Reason}},
- fatal("~p~n", [Error]),
+ fatal("~tp~n", [Error]),
{noreply, State}
end;
@@ -461,7 +461,7 @@ handle_call(init, _From, State) ->
{reply, EarlyNodes, State2};
handle_call(Msg, _From, State) ->
- error("~p got unexpected call: ~p~n", [?MODULE, Msg]),
+ error("~p got unexpected call: ~tp~n", [?MODULE, Msg]),
{noreply, State}.
accept_protocol(Mon, Version, Protocol, From, State) ->
@@ -535,7 +535,7 @@ handle_cast({inconsistent_database, Context, Node}, State) ->
{noreply, State};
handle_cast(Msg, State) ->
- error("~p got unexpected cast: ~p~n", [?MODULE, Msg]),
+ error("~p got unexpected cast: ~tp~n", [?MODULE, Msg]),
{noreply, State}.
%%----------------------------------------------------------------------
@@ -572,7 +572,7 @@ handle_info(Msg = {'EXIT',Pid,_}, State) ->
%% We have probably got an exit signal from
%% disk_log or dets
Hint = "Hint: check that the disk still is writable",
- fatal("~p got unexpected info: ~p; ~p~n",
+ fatal("~p got unexpected info: ~tp; ~p~n",
[?MODULE, Msg, Hint])
end;
@@ -599,13 +599,13 @@ handle_info({disk_log, _Node, Log, Info}, State) ->
{truncated, _No} ->
ok;
_ ->
- mnesia_lib:important("Warning Log file ~p error reason ~s~n",
+ mnesia_lib:important("Warning Log file ~tp error reason ~ts~n",
[Log, disk_log:format_error(Info)])
end,
{noreply, State};
handle_info(Msg, State) ->
- error("~p got unexpected info (~p): ~p~n", [?MODULE, State, Msg]).
+ error("~p got unexpected info (~tp): ~tp~n", [?MODULE, State, Msg]).
process_q(State = #state{mq=[]}) -> {noreply,State};
process_q(State = #state{mq=[{info,Msg}|R]}) ->
diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl
index b204fb282f..d792070332 100644
--- a/lib/mnesia/src/mnesia_recover.erl
+++ b/lib/mnesia/src/mnesia_recover.erl
@@ -762,7 +762,7 @@ handle_call(sync, _From, State) ->
{reply, ok, State};
handle_call(Msg, _From, State) ->
- error("~p got unexpected call: ~p~n", [?MODULE, Msg]),
+ error("~p got unexpected call: ~tp~n", [?MODULE, Msg]),
{noreply, State}.
do_log_mnesia_up(Node) ->
@@ -881,7 +881,7 @@ handle_cast({log_dump_overload, Flag}, State) when is_boolean(Flag) ->
{noreply, State#state{log_dump_overload = Flag}};
handle_cast(Msg, State) ->
- error("~p got unexpected cast: ~p~n", [?MODULE, Msg]),
+ error("~p got unexpected cast: ~tp~n", [?MODULE, Msg]),
{noreply, State}.
%%----------------------------------------------------------------------
@@ -927,11 +927,11 @@ handle_info({force_decision, Tid}, State) ->
end;
handle_info({'EXIT', Pid, R}, State) when Pid == State#state.supervisor ->
- mnesia_lib:dbg_out("~p was ~p~n",[?MODULE, R]),
+ mnesia_lib:dbg_out("~p was ~tp~n",[?MODULE, R]),
{stop, shutdown, State};
handle_info(Msg, State) ->
- error("~p got unexpected info: ~p~n", [?MODULE, Msg]),
+ error("~p got unexpected info: ~tp~n", [?MODULE, Msg]),
{noreply, State}.
%%----------------------------------------------------------------------
diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl
index f71ee26d7c..83cc19c678 100644
--- a/lib/mnesia/src/mnesia_schema.erl
+++ b/lib/mnesia/src/mnesia_schema.erl
@@ -386,7 +386,7 @@ delete_schema(Ns) when is_list(Ns), Ns /= [] ->
[] ->
ok;
BadReplies ->
- verbose("~s: ~p~n", [Reason, BadReplies]),
+ verbose("~s: ~tp~n", [Reason, BadReplies]),
{error, {"All nodes not running", BadReplies}}
end;
{_Replies, BadNs} ->
@@ -467,10 +467,10 @@ opt_create_dir(UseDir, Dir) when UseDir == true->
false ->
case file:make_dir(Dir) of
ok ->
- verbose("Create Directory ~p~n", [Dir]),
+ verbose("Create Directory ~tp~n", [Dir]),
ok;
{error, Reason} ->
- verbose("Cannot create mnesia dir ~p~n", [Reason]),
+ verbose("Cannot create mnesia dir ~tp~n", [Reason]),
{error, {"Cannot create Mnesia dir", Dir, Reason}}
end
end;
@@ -1470,7 +1470,7 @@ verify_backend_type(Name, Module) ->
[] ->
ok;
_Other ->
- io:fwrite(user, "Missing backend_type exports: ~p~n", [_Other]),
+ io:fwrite(user, "Missing backend_type exports: ~tp~n", [_Other]),
mnesia:abort({bad_type, {backend_type,Name,Module}})
end.
@@ -1776,7 +1776,7 @@ make_del_table_copy(Tab, Node) ->
mnesia:abort({combine_error, Tab, "Last replica"});
[] ->
ensure_active(Cs),
- dbg_out("Last replica deleted in table ~p~n", [Tab]),
+ dbg_out("Last replica deleted in table ~tp~n", [Tab]),
make_delete_table(Tab, whole_table);
_ when Tab == schema ->
%% ensure_active(Cs2),
@@ -2178,13 +2178,13 @@ do_write_table_property(Tab, Prop) ->
case change_prop_in_existing_op(Tab, Prop, write_property, Store) of
true ->
dbg_out("change_prop_in_existing_op"
- "(~p,~p,write_property,Store) -> true~n",
+ "(~tp,~p,write_property,Store) -> true~n",
[Tab,Prop]),
%% we have merged the table prop into the create_table op
ok;
false ->
dbg_out("change_prop_in_existing_op"
- "(~p,~p,write_property,Store) -> false~n",
+ "(~tp,~p,write_property,Store) -> false~n",
[Tab,Prop]),
%% this must be an existing table
get_tid_ts_and_lock(Tab, none),
@@ -2315,13 +2315,13 @@ do_delete_table_property(Tab, PropKey) ->
case change_prop_in_existing_op(Tab, PropKey, delete_property, Store) of
true ->
dbg_out("change_prop_in_existing_op"
- "(~p,~p,delete_property,Store) -> true~n",
+ "(~tp,~p,delete_property,Store) -> true~n",
[Tab,PropKey]),
%% we have merged the table prop into the create_table op
ok;
false ->
dbg_out("change_prop_in_existing_op"
- "(~p,~p,delete_property,Store) -> false~n",
+ "(~tp,~p,delete_property,Store) -> false~n",
[Tab,PropKey]),
%% this must be an existing table
get_tid_ts_and_lock(Tab, none),
@@ -2435,17 +2435,17 @@ prepare_op(_Tid, {op, sync_trans}, {part, CoordPid}) ->
{sync_trans, CoordPid} ->
{false, optional};
{mnesia_down, _Node} = Else ->
- mnesia_lib:verbose("sync_op terminated due to ~p~n", [Else]),
+ mnesia_lib:verbose("sync_op terminated due to ~tp~n", [Else]),
mnesia:abort(Else);
{'EXIT', _, _} = Else ->
- mnesia_lib:verbose("sync_op terminated due to ~p~n", [Else]),
+ mnesia_lib:verbose("sync_op terminated due to ~tp~n", [Else]),
mnesia:abort(Else)
end;
prepare_op(_Tid, {op, sync_trans}, {coord, Nodes}) ->
case receive_sync(Nodes, []) of
{abort, Reason} ->
- mnesia_lib:verbose("sync_op terminated due to ~p~n", [Reason]),
+ mnesia_lib:verbose("sync_op terminated due to ~tp~n", [Reason]),
mnesia:abort(Reason);
Pids ->
[Pid ! {sync_trans, self()} || Pid <- Pids],
@@ -2707,7 +2707,7 @@ prepare_op(_Tid, {op, transform, Fun, TabDef}, _WaitFor) ->
{true, Objs, mandatory}
catch _:Reason ->
mnesia_lib:db_fixtable(Storage, Tab, false),
- mnesia_lib:important("Transform function failed: '~p' in '~p'",
+ mnesia_lib:important("Transform function failed: '~tp' in '~tp'",
[Reason, erlang:get_stacktrace()]),
exit({"Bad transform function", Tab, Fun, node(), Reason})
end
@@ -2719,7 +2719,7 @@ prepare_op(_Tid, {op, merge_schema, TabDef}, _WaitFor) ->
ok ->
{true, optional};
Error ->
- verbose("Merge_Schema ~p failed on ~p: ~p~n", [_Tid,node(),Error]),
+ verbose("Merge_Schema ~p failed on ~p: ~tp~n", [_Tid,node(),Error]),
mnesia:abort({bad_commit, Error})
end;
prepare_op(_Tid, _Op, _WaitFor) ->
@@ -3133,7 +3133,7 @@ ext_real_suffixes(Ext) ->
[M || {_,M} <- Ext])
catch
error:E ->
- verbose("Cant find real ext suffixes (~p)~n", [E]),
+ verbose("Cant find real ext suffixes (~tp)~n", [E]),
[]
end.
@@ -3142,7 +3142,7 @@ ext_tmp_suffixes(Ext) ->
[M || {_,M} <- Ext])
catch
error:E ->
- verbose("Cant find tmp ext suffixes (~p)~n", [E]),
+ verbose("Cant find tmp ext suffixes (~tp)~n", [E]),
[]
end.
@@ -3153,14 +3153,14 @@ info() ->
info(Tab) ->
Props = get_table_properties(Tab),
- io:format("-- Properties for ~w table --- ~n",[Tab]),
+ io:format("-- Properties for ~tw table --- ~n",[Tab]),
info2(Tab, Props).
info2(Tab, [{cstruct, _V} | Tail]) -> % Ignore cstruct
info2(Tab, Tail);
info2(Tab, [{frag_hash, _V} | Tail]) -> % Ignore frag_hash
info2(Tab, Tail);
info2(Tab, [{P, V} | Tail]) ->
- io:format("~-20w -> ~p~n",[P,V]),
+ io:format("~-20tw -> ~tp~n",[P,V]),
info2(Tab, Tail);
info2(_, []) ->
io:format("~n", []).
@@ -3726,7 +3726,7 @@ merge_versions(AnythingNew, Cs, RemoteCs, Force) ->
ok;
true ->
Str = io_lib:format("Bad cookies. Cannot merge definitions of "
- "table ~w. Local = ~w, Remote = ~w~n",
+ "table ~tw. Local = ~w, Remote = ~w~n",
[Cs#cstruct.name, Cs, RemoteCs]),
throw(Str)
end,
@@ -3746,7 +3746,7 @@ merge_versions(AnythingNew, Cs, RemoteCs, Force) ->
do_merge_versions(AnythingNew, Cs, RemoteCs);
true ->
Str1 = io_lib:format("Cannot merge definitions of "
- "table ~w. Local = ~w, Remote = ~w~n",
+ "table ~tw. Local = ~w, Remote = ~w~n",
[Cs#cstruct.name, Cs, RemoteCs]),
throw(Str1)
end.
diff --git a/lib/mnesia/src/mnesia_subscr.erl b/lib/mnesia/src/mnesia_subscr.erl
index c2748f5bae..dfaa20d2d3 100644
--- a/lib/mnesia/src/mnesia_subscr.erl
+++ b/lib/mnesia/src/mnesia_subscr.erl
@@ -264,7 +264,7 @@ handle_call({change, How}, _From, State) ->
{reply, Reply, State};
handle_call(Msg, _From, State) ->
- error("~p got unexpected call: ~p~n", [?MODULE, Msg]),
+ error("~p got unexpected call: ~tp~n", [?MODULE, Msg]),
{noreply, State}.
%%----------------------------------------------------------------------
@@ -274,7 +274,7 @@ handle_call(Msg, _From, State) ->
%% {stop, Reason, State} (terminate/2 is called)
%%----------------------------------------------------------------------
handle_cast(Msg, State) ->
- error("~p got unexpected cast: ~p~n", [?MODULE, Msg]),
+ error("~p got unexpected cast: ~tp~n", [?MODULE, Msg]),
{noreply, State}.
%%----------------------------------------------------------------------
@@ -292,7 +292,7 @@ handle_info({'EXIT', Pid, _Reason}, State) ->
{noreply, State};
handle_info(Msg, State) ->
- error("~p got unexpected info: ~p~n", [?MODULE, Msg]),
+ error("~p got unexpected info: ~tp~n", [?MODULE, Msg]),
{noreply, State}.
%%----------------------------------------------------------------------
diff --git a/lib/mnesia/src/mnesia_text.erl b/lib/mnesia/src/mnesia_text.erl
index 21adca813a..7d24d09472 100644
--- a/lib/mnesia/src/mnesia_text.erl
+++ b/lib/mnesia/src/mnesia_text.erl
@@ -87,18 +87,18 @@ validate_tab(_) -> error(badtab).
make_tabs([{Tab, Def} | Tail]) ->
try mnesia:table_info(Tab, where_to_read) of
Node ->
- io:format("** Table ~w already exists on ~p, just entering data~n",
+ io:format("** Table ~tw already exists on ~p, just entering data~n",
[Tab, Node]),
make_tabs(Tail)
catch exit:_ -> %% non-existing table
case mnesia:create_table(Tab, Def) of
{aborted, Reason} ->
- io:format("** Failed to create table ~w ~n"
- "** Reason = ~w, Args = ~p~n",
+ io:format("** Failed to create table ~tw ~n"
+ "** Reason = ~tw, Args = ~tp~n",
[Tab, Reason, Def]),
[Tab | make_tabs(Tail)];
_ ->
- io:format("New table ~w~n", [Tab]),
+ io:format("New table ~tw~n", [Tab]),
make_tabs(Tail)
end
end;
@@ -139,12 +139,12 @@ collect_data(Tabs, [{Line, Term} | Tail]) when is_tuple(Term) ->
{value, _} ->
[Term | collect_data(Tabs, Tail)];
_Other ->
- io:format("Object:~p at line ~w unknown\n", [Term,Line]),
+ io:format("Object:~tp at line ~w unknown\n", [Term,Line]),
error(undefined_object)
end;
collect_data(_Tabs, []) -> [];
collect_data(_Tabs, [H|_T]) ->
- io:format("Object:~p unknown\n", [H]),
+ io:format("Object:~tp unknown\n", [H]),
error(undefined_object).
error(What) -> throw({error, What}).
@@ -178,7 +178,7 @@ read_term_from_stream(Stream, File, Line) ->
{ok, {Line, Term}, EndLine};
{error, {NewLine,Mod,What}} ->
Str = Mod:format_error(What),
- io:format("Error in line:~p of:~p ~s\n",
+ io:format("Error in line:~p of:~tp ~ts\n",
[NewLine, File, Str]),
error
end;
diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
index 305bf14bcf..ebf580d09e 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.erl
@@ -314,7 +314,7 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=
?eval_debug_fun({?MODULE, do_abort, pre}, [{tid, Tid}]),
case gb_trees:lookup(Tid, Participants) of
none ->
- verbose("Tried to abort a non participant transaction ~p: ~p~n",
+ verbose("Tried to abort a non participant transaction ~p: ~tp~n",
[Tid, Reason]),
mnesia_locker:release_tid(Tid),
doit_loop(State);
@@ -417,7 +417,7 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=
{From, {unblock_me, Tab}} ->
case lists:member(Tab, State#state.blocked_tabs) of
false ->
- verbose("Wrong dirty Op blocked on ~p ~p ~p",
+ verbose("Wrong dirty Op blocked on ~p ~tp ~p",
[node(), Tab, From]),
reply(From, unblocked),
doit_loop(State);
@@ -466,11 +466,11 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=
end;
{system, From, Msg} ->
- dbg_out("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
+ dbg_out("~p got {system, ~p, ~tp}~n", [?MODULE, From, Msg]),
sys:handle_system_msg(Msg, From, Sup, ?MODULE, [], State);
Msg ->
- verbose("** ERROR ** ~p got unexpected message: ~p~n", [?MODULE, Msg]),
+ verbose("** ERROR ** ~p got unexpected message: ~tp~n", [?MODULE, Msg]),
doit_loop(State)
end.
@@ -556,7 +556,7 @@ handle_exit(Pid, Reason, State) ->
%% We got exit from a local fool
doit_loop(State);
{P = #participant{}, _RestP} ->
- fatal("Participant ~p in transaction ~p died ~p~n",
+ fatal("Participant ~p in transaction ~p died ~tp~n",
[P#participant.pid, P#participant.tid, Reason]),
NewPs = gb_trees:delete(P#participant.tid,State#state.participants),
doit_loop(State#state{participants = NewPs})
@@ -598,7 +598,7 @@ recover_coordinator(Tid, Etabs) ->
ok %% to the new nested trans store.
end
catch _:Reason ->
- dbg_out("Recovery of coordinator ~p failed:~n",
+ dbg_out("Recovery of coordinator ~p failed: ~tp~n",
[Tid, {Reason, erlang:get_stacktrace()}]),
Protocol = asym_trans,
tell_outcome(Tid, Protocol, node(), CheckNodes, TellNodes)
@@ -941,7 +941,7 @@ decr(_X) -> 0.
return_abort(Fun, Args, Reason) ->
{_Mod, Tid, Ts} = get(mnesia_activity_state),
- dbg_out("Transaction ~p calling ~p with ~p failed: ~n ~p~n",
+ dbg_out("Transaction ~p calling ~tp with ~tp failed: ~n ~tp~n",
[Tid, Fun, Args, Reason]),
OldStore = Ts#tidstore.store,
Nodes = get_elements(nodes, OldStore),
@@ -1714,7 +1714,7 @@ commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) ->
mnesia_schema:undo_prepare_commit(Tid, C0);
Msg ->
- verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~p~n",
+ verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~tp~n",
[Tid, Msg])
end;
{Tid, {do_abort, Reason}} ->
@@ -1730,7 +1730,7 @@ commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) ->
Msg ->
reply(Coord, {do_abort, Tid, self(), {bad_commit,internal}}),
- verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~p~n",
+ verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~tp~n",
[Tid, Msg])
end
catch _:Reason ->
@@ -1804,7 +1804,7 @@ do_update(Tid, Storage, [Op | Ops], OldRes) ->
%% Determine actual storage type and try again.
%% BUGBUG: Updates may be lost if table is transformed.
ST = erlang:get_stacktrace(),
- verbose("do_update in ~w failed: ~p -> {'EXIT', ~p}~n",
+ verbose("do_update in ~w failed: ~tp -> {'EXIT', ~tp}~n",
[Tid, Op, {Reason, ST}]),
do_update(Tid, Storage, Ops, OldRes)
end;
@@ -1919,7 +1919,7 @@ do_snmp(Tid, [Head|Tail]) ->
%% deleted our local replica or recently deattached
%% the snmp table
ST = erlang:get_stacktrace(),
- verbose("do_snmp in ~w failed: ~p -> {'EXIT', ~p}~n",
+ verbose("do_snmp in ~w failed: ~tp -> {'EXIT', ~tp}~n",
[Tid, Head, {Reason, ST}])
end,
do_snmp(Tid, Tail).
@@ -2151,7 +2151,7 @@ pr_participant(Stream, P) ->
true -> Commit0
end,
pr_tid(Stream, P#participant.tid),
- io:format(Stream, "with participant objects ~p~n", [Commit]).
+ io:format(Stream, "with participant objects ~tp~n", [Commit]).
pr_tid(Stream, Tid) ->
@@ -2193,7 +2193,7 @@ search_pr_participant(S, [ P | Tail]) ->
true -> Commit0
end,
- io:format("~p~n", [Commit]),
+ io:format("~tp~n", [Commit]),
search_pr_participant(S,Tail); %% !!!!!
true ->
search_pr_participant(S, Tail)
@@ -2214,12 +2214,12 @@ display_pid_info(Pid) ->
Reds = fetch(reductions, Info),
LM = length(fetch(messages, Info)),
pformat(io_lib:format("~p", [Pid]),
- io_lib:format("~p", [Call]),
- io_lib:format("~p", [Curr]), Reds, LM)
+ io_lib:format("~tp", [Call]),
+ io_lib:format("~tp", [Curr]), Reds, LM)
end.
pformat(A1, A2, A3, A4, A5) ->
- io:format( "~-12s ~-21s ~-21s ~9w ~4w~n", [A1,A2,A3,A4,A5]).
+ io:format( "~-12s ~-21ts ~-21ts ~9w ~4w~n", [A1,A2,A3,A4,A5]).
fetch(Key, Info) ->
case lists:keysearch(Key, 1, Info) of
diff --git a/lib/observer/src/cdv_atom_cb.erl b/lib/observer/src/cdv_atom_cb.erl
index a123354c8f..86cdf2fd6d 100644
--- a/lib/observer/src/cdv_atom_cb.erl
+++ b/lib/observer/src/cdv_atom_cb.erl
@@ -42,7 +42,7 @@ get_info(_) ->
{Info,TW}.
format({Bin,q}) when is_binary(Bin) ->
- [$'|binary_to_list(Bin)];
+ [$'|lists:flatten(io_lib:format("~ts",[Bin]))];
format({Bin,nq}) when is_binary(Bin) ->
lists:flatten(io_lib:format("~ts",[Bin]));
format(D) ->
diff --git a/lib/observer/src/cdv_bin_cb.erl b/lib/observer/src/cdv_bin_cb.erl
index 5472d36a6f..5502869973 100644
--- a/lib/observer/src/cdv_bin_cb.erl
+++ b/lib/observer/src/cdv_bin_cb.erl
@@ -38,6 +38,7 @@ init_bin_page(Parent,{Type,Bin}) ->
[{"Format \~p",cdv_html_wx,{Type,format_bin_fun("~p",Bin)}},
{"Format \~tp",cdv_html_wx,{Type,format_bin_fun("~tp",Bin)}},
{"Format \~w",cdv_html_wx,{Type,format_bin_fun("~w",Bin)}},
+ {"Format \~tw",cdv_html_wx,{Type,format_bin_fun("~tw",Bin)}},
{"Format \~s",cdv_html_wx,{Type,format_bin_fun("~s",Bin)}},
{"Format \~ts",cdv_html_wx,{Type,format_bin_fun("~ts",Bin)}},
{"Hex",cdv_html_wx,{Type,hex_binary_fun(Bin)}},
@@ -56,7 +57,7 @@ format_bin_fun(Format,Bin) ->
binary_to_term_fun(Bin) ->
fun() ->
try binary_to_term(Bin) of
- Term -> plain_html(io_lib:format("~p",[Term]))
+ Term -> plain_html(io_lib:format("~tp",[Term]))
catch error:badarg ->
Warning = "This binary can not be converted to an Erlang term",
observer_html_lib:warning(Warning)
diff --git a/lib/observer/src/cdv_detail_wx.erl b/lib/observer/src/cdv_detail_wx.erl
index 27057fd27f..4c26e447a6 100644
--- a/lib/observer/src/cdv_detail_wx.erl
+++ b/lib/observer/src/cdv_detail_wx.erl
@@ -133,7 +133,7 @@ handle_event(Event, _State) ->
error({unhandled_event, Event}).
handle_info(_Info, State) ->
- %% io:format("~p: ~p, Handle info: ~p~n", [?MODULE, ?LINE, _Info]),
+ %% io:format("~p: ~p, Handle info: ~tp~n", [?MODULE, ?LINE, _Info]),
{noreply, State}.
handle_call(Call, From, _State) ->
diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl
index 0ab0ba4315..5158e95a65 100644
--- a/lib/observer/src/cdv_html_wx.erl
+++ b/lib/observer/src/cdv_html_wx.erl
@@ -62,7 +62,7 @@ handle_info(active, State) ->
{noreply, State};
handle_info(Info, State) ->
- io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]),
+ io:format("~p:~p: Unhandled info: ~tp~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
terminate(_Reason, _State) ->
@@ -72,7 +72,7 @@ code_change(_, _, State) ->
{ok, State}.
handle_call(Msg, _From, State) ->
- io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p~p: Unhandled Call ~tp~n",[?MODULE, ?LINE, Msg]),
{reply, ok, State}.
handle_cast({detail_win_closed, Id},#state{expand_wins=Opened0}=State) ->
@@ -80,7 +80,7 @@ handle_cast({detail_win_closed, Id},#state{expand_wins=Opened0}=State) ->
{noreply, State#state{expand_wins=Opened}};
handle_cast(Msg, State) ->
- io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p~p: Unhandled cast ~tp~n",[?MODULE, ?LINE, Msg]),
{noreply, State}.
handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked,
@@ -118,7 +118,7 @@ handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked,
{noreply, NewState};
handle_event(Event, State) ->
- io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]),
+ io:format("~p:~p: Unhandled event ~tp\n", [?MODULE,?LINE,Event]),
{noreply, State}.
%%%-----------------------------------------------------------------
diff --git a/lib/observer/src/cdv_info_wx.erl b/lib/observer/src/cdv_info_wx.erl
index 01fe6b15f2..7e416dd11a 100644
--- a/lib/observer/src/cdv_info_wx.erl
+++ b/lib/observer/src/cdv_info_wx.erl
@@ -65,7 +65,7 @@ handle_info(active, State) ->
{noreply, State};
handle_info(Info, State) ->
- io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]),
+ io:format("~p:~p: Unhandled info: ~tp~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
terminate(_Reason, _State) ->
@@ -88,11 +88,11 @@ handle_call(new_dump, _From, #state{callback=Callback,panel=Panel,
{reply, ok, State#state{fpanel=NewFPanel,trunc_warn=TW}};
handle_call(Msg, _From, State) ->
- io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p~p: Unhandled Call ~tp~n",[?MODULE, ?LINE, Msg]),
{reply, ok, State}.
handle_cast(Msg, State) ->
- io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p~p: Unhandled cast ~tp~n",[?MODULE, ?LINE, Msg]),
{noreply, State}.
handle_event(#wx{event=#wxMouse{type=left_down},userData=Target}, State) ->
@@ -108,7 +108,7 @@ handle_event(#wx{obj=Obj,event=#wxMouse{type=leave_window}},State) ->
{noreply, State};
handle_event(Event, State) ->
- io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]),
+ io:format("~p:~p: Unhandled event ~tp\n", [?MODULE,?LINE,Event]),
{noreply, State}.
%%%-----------------------------------------------------------------
diff --git a/lib/observer/src/cdv_multi_wx.erl b/lib/observer/src/cdv_multi_wx.erl
index b511503752..93f045b1da 100644
--- a/lib/observer/src/cdv_multi_wx.erl
+++ b/lib/observer/src/cdv_multi_wx.erl
@@ -94,7 +94,7 @@ handle_info(active, State) ->
{noreply, NewState};
handle_info(Info, State) ->
- io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]),
+ io:format("~p:~p: Unhandled info: ~tp~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
terminate(_Reason, _State) ->
@@ -112,11 +112,11 @@ handle_call(new_dump, _From, State) ->
{reply, ok, NewState};
handle_call(Msg, _From, State) ->
- io:format("~p:~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p:~p: Unhandled Call ~tp~n",[?MODULE, ?LINE, Msg]),
{reply, ok, State}.
handle_cast(Msg, State) ->
- io:format("~p:~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p:~p: Unhandled cast ~tp~n",[?MODULE, ?LINE, Msg]),
{noreply, State}.
handle_event(#wx{event=#wxCommand{type=command_listbox_selected,
@@ -136,7 +136,7 @@ handle_event(#wx{event=#wxCommand{type=command_listbox_selected,
{noreply,NewState};
handle_event(Event, State) ->
- io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]),
+ io:format("~p:~p: Unhandled event ~tp\n", [?MODULE,?LINE,Event]),
{noreply, State}.
%%%%%%%%%%%%%%%%%%%%%%% Internal %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/src/cdv_proc_cb.erl b/lib/observer/src/cdv_proc_cb.erl
index 592150146b..f10650bbb7 100644
--- a/lib/observer/src/cdv_proc_cb.erl
+++ b/lib/observer/src/cdv_proc_cb.erl
@@ -71,7 +71,7 @@ get_details(Id, _) ->
Proplist0 =
crashdump_viewer:to_proplist(record_info(fields,proc),Info),
Proplist = [{expand_table,Tab}|Proplist0],
- Title = io_lib:format("~s (~s)",[Info#proc.name, Id]),
+ Title = io_lib:format("~ts (~s)",[Info#proc.name, Id]),
{ok,{Title,Proplist,TW}};
{error,{other_node,NodeId}} ->
Info = "The process you are searching for was residing on "
diff --git a/lib/observer/src/cdv_table_wx.erl b/lib/observer/src/cdv_table_wx.erl
index df16230b70..ba23758ea6 100644
--- a/lib/observer/src/cdv_table_wx.erl
+++ b/lib/observer/src/cdv_table_wx.erl
@@ -74,7 +74,7 @@ handle_info(active, State) ->
{noreply, State};
handle_info(Info, State) ->
- io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]),
+ io:format("~p:~p: Unhandled info: ~tp~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
terminate(_Reason, _State) ->
@@ -84,15 +84,15 @@ code_change(_, _, State) ->
{ok, State}.
handle_call(Msg, _From, State) ->
- io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p~p: Unhandled Call ~tp~n",[?MODULE, ?LINE, Msg]),
{reply, ok, State}.
handle_cast(Msg, State) ->
- io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p~p: Unhandled cast ~tp~n",[?MODULE, ?LINE, Msg]),
{noreply, State}.
handle_event(Event, State) ->
- io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]),
+ io:format("~p:~p: Unhandled event ~tp\n", [?MODULE,?LINE,Event]),
{noreply, State}.
%%%%%%%%%%%%%%%%%%%%%%% Internal %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/src/cdv_term_cb.erl b/lib/observer/src/cdv_term_cb.erl
index f0d90dde7c..f206d7e4c9 100644
--- a/lib/observer/src/cdv_term_cb.erl
+++ b/lib/observer/src/cdv_term_cb.erl
@@ -57,13 +57,7 @@ expand(['#CDVBin',Offset,Size,Pos], true) ->
{ok,Bin} = crashdump_viewer:expand_binary({Offset,Size,Pos}),
Bin;
expand(Bin, Tab) when is_binary(Bin), not is_boolean(Tab) ->
- Size = byte_size(Bin),
- PrevSize = min(Size, 10) * 8,
- <<Preview:PrevSize, _/binary>> = Bin,
- Hash = erlang:phash2(Bin),
- Key = {Preview, Size, Hash},
- ets:insert(Tab, {Key,Bin}),
- ['#OBSBin',Preview,Size,Hash];
+ observer_lib:make_obsbin(Bin, Tab);
expand([H|T], Expand) ->
case expand(T, Expand) of
ET when is_list(ET) ->
diff --git a/lib/observer/src/cdv_virtual_list_wx.erl b/lib/observer/src/cdv_virtual_list_wx.erl
index ebf58865e9..f3daae8e4d 100644
--- a/lib/observer/src/cdv_virtual_list_wx.erl
+++ b/lib/observer/src/cdv_virtual_list_wx.erl
@@ -73,7 +73,7 @@ start_detail_win(Id) ->
"#Port"++_ ->
start_detail_win(Id, port);
_ ->
- io:format("cdv: unknown identifier: ~p~n",[Id]),
+ io:format("cdv: unknown identifier: ~tp~n",[Id]),
ignore
end.
@@ -195,7 +195,7 @@ call(Holder, What) when is_pid(Holder) ->
erlang:demonitor(Ref),
Res
after 5000 ->
- io:format("Hanging call ~p~n",[What]),
+ io:format("Hanging call ~tp~n",[What]),
""
end;
call(_,_) ->
@@ -214,7 +214,7 @@ handle_info(active, State) ->
{noreply, State};
handle_info(Info, State) ->
- io:format("~p:~p, Unexpected info: ~p~n", [?MODULE, ?LINE, Info]),
+ io:format("~p:~p, Unexpected info: ~tp~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
terminate(_Reason, #state{holder=Holder}) ->
@@ -236,7 +236,7 @@ handle_call(new_dump, _From,
{reply, ok, State#state{detail_wins=[],holder=NewHolder,trunc_warn=TW}};
handle_call(Msg, _From, State) ->
- io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p:~p: Unhandled call ~tp~n",[?MODULE, ?LINE, Msg]),
{reply, ok, State}.
handle_cast({start_detail_win,Id}, State) ->
@@ -248,7 +248,7 @@ handle_cast({detail_win_closed, Id},#state{detail_wins=Opened}=State) ->
{noreply, State#state{detail_wins=Opened2}};
handle_cast(Msg, State) ->
- io:format("~p:~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]),
+ io:format("~p:~p: Unhandled cast ~tp~n", [?MODULE, ?LINE, Msg]),
{noreply, State}.
%%%%%%%%%%%%%%%%%%%%LOOP%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -322,7 +322,7 @@ handle_event(#wx{event=#wxList{type=command_list_item_activated,
{noreply, State};
handle_event(Event, State) ->
- io:format("~p:~p: handle event ~p\n", [?MODULE, ?LINE, Event]),
+ io:format("~p:~p: handle event ~tp\n", [?MODULE, ?LINE, Event]),
{noreply, State}.
@@ -382,7 +382,7 @@ table_holder(#holder{callback=Callback, attrs=Attrs, info=Info}=S0) ->
stop ->
ok;
What ->
- io:format("Table holder got ~p~n",[What]),
+ io:format("Table holder got ~tp~n",[What]),
table_holder(S0)
end.
diff --git a/lib/observer/src/cdv_wx.erl b/lib/observer/src/cdv_wx.erl
index 1e3fb6289e..898f39ded2 100644
--- a/lib/observer/src/cdv_wx.erl
+++ b/lib/observer/src/cdv_wx.erl
@@ -306,7 +306,7 @@ handle_info({'EXIT', Pid, normal}, #state{server=Pid}=State) ->
{stop, normal, State};
handle_info({'EXIT', Pid, _Reason}, State) ->
- io:format("Child (~s) crashed exiting: ~p ~p~n",
+ io:format("Child (~s) crashed exiting: ~p ~tp~n",
[pid2panel(Pid, State), Pid,_Reason]),
{stop, normal, State};
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 8d70f5e2c8..fd8b3b9e67 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -200,13 +200,13 @@ do_script_start(StartFun) ->
{'EXIT', Pid, normal} ->
ok;
{'EXIT', Pid, Reason} ->
- io:format("\ncdv crash: ~p\n",[Reason])
+ io:format("\ncdv crash: ~tp\n",[Reason])
end;
_ ->
io:format("\ncdv crash: ~p\n",[unknown_reason])
end;
Error ->
- io:format("\ncdv start failed: ~p\n",[Error])
+ io:format("\ncdv start failed: ~tp\n",[Error])
end.
usage() ->
@@ -340,7 +340,7 @@ handle_call(general_info,_From,State=#state{file=File}) ->
handle_call({expand_binary,{Offset,Size,Pos}},_From,State=#state{file=File}) ->
Fd = open(File),
pos_bof(Fd,Pos),
- {Bin,_Line} = get_binary(Offset,Size,val(Fd)),
+ {Bin,_Line} = get_binary(Offset,Size,bytes(Fd)),
close(Fd),
{reply,{ok,Bin},State};
handle_call(procs_summary,_From,State=#state{file=File,wordsize=WS}) ->
@@ -512,9 +512,9 @@ unexpected(_Fd,{eof,_LastLine},_Where) ->
ok; % truncated file
unexpected(Fd,{part,What},Where) ->
skip_rest_of_line(Fd),
- io:format("WARNING: Found unexpected line in ~s:~n~s ...~n",[Where,What]);
+ io:format("WARNING: Found unexpected line in ~ts:~n~ts ...~n",[Where,What]);
unexpected(_Fd,What,Where) ->
- io:format("WARNING: Found unexpected line in ~s:~n~s~n",[Where,What]).
+ io:format("WARNING: Found unexpected line in ~ts:~n~ts~n",[Where,What]).
truncated_warning([]) ->
[];
@@ -701,9 +701,24 @@ skip(Fd,<<>>) ->
end.
-val(Fd) ->
- val(Fd, "-1").
-val(Fd, NoExist) ->
+string(Fd) ->
+ string(Fd, "-1").
+string(Fd,NoExist) ->
+ case bytes(Fd,noexist) of
+ noexist -> NoExist;
+ Val -> byte_list_to_string(Val)
+ end.
+
+byte_list_to_string(ByteList) ->
+ Bin = list_to_binary(ByteList),
+ case unicode:characters_to_list(Bin) of
+ Str when is_list(Str) -> Str;
+ _ -> ByteList
+ end.
+
+bytes(Fd) ->
+ bytes(Fd, "-1").
+bytes(Fd, NoExist) ->
case get_rest_of_line(Fd) of
{eof,[]} -> NoExist;
[] -> NoExist;
@@ -742,7 +757,7 @@ get_lines_to_empty(Fd,<<$\n:8,Bin/binary>>,[],Lines) ->
put_chunk(Fd,Bin),
lists:reverse(Lines);
get_lines_to_empty(Fd,<<$\n:8,Bin/binary>>,Acc,Lines) ->
- get_lines_to_empty(Fd,Bin,[],[lists:reverse(Acc)|Lines]);
+ get_lines_to_empty(Fd,Bin,[],[byte_list_to_string(lists:reverse(Acc))|Lines]);
get_lines_to_empty(Fd,<<$\r:8,Bin/binary>>,Acc,Lines) ->
get_lines_to_empty(Fd,Bin,Acc,Lines);
get_lines_to_empty(Fd,<<$\s:8,Bin/binary>>,[],Lines) ->
@@ -754,7 +769,7 @@ get_lines_to_empty(Fd,<<>>,Acc,Lines) ->
{ok,Bin} ->
get_lines_to_empty(Fd,Bin,Acc,Lines);
eof ->
- lists:reverse(Lines,[lists:reverse(Acc)])
+ lists:reverse(Lines,[byte_list_to_string(lists:reverse(Acc))])
end.
split(Str) ->
@@ -816,7 +831,7 @@ do_read_file(File) ->
{ok,Binaries,DumpVsn};
_Other ->
R = io_lib:format(
- "~s is not an Erlang crash dump~n",
+ "~ts is not an Erlang crash dump~n",
[File]),
close(Fd),
{error,R}
@@ -824,20 +839,20 @@ do_read_file(File) ->
{ok,<<"<Erlang crash dump>",_Rest/binary>>} ->
%% old version - no longer supported
R = io_lib:format(
- "The crashdump ~s is in the pre-R10B format, "
+ "The crashdump ~ts is in the pre-R10B format, "
"which is no longer supported.~n",
[File]),
close(Fd),
{error,R};
_Other ->
R = io_lib:format(
- "~s is not an Erlang crash dump~n",
+ "~ts is not an Erlang crash dump~n",
[File]),
close(Fd),
{error,R}
end;
_other ->
- R = io_lib:format("~s is not an Erlang crash dump~n",[File]),
+ R = io_lib:format("~ts is not an Erlang crash dump~n",[File]),
{error,R}
end.
@@ -986,7 +1001,7 @@ general_info(File) ->
instr_info=InstrInfo}.
get_slogan_and_sysvsn(Fd,Acc) ->
- case val(Fd,eof) of
+ case string(Fd,eof) of
"Slogan: " ++ SloganPart when Acc==[] ->
get_slogan_and_sysvsn(Fd,[SloganPart]);
"System version: " ++ SystemVsn ->
@@ -1000,14 +1015,14 @@ get_slogan_and_sysvsn(Fd,Acc) ->
get_general_info(Fd,GenInfo) ->
case line_head(Fd) of
"Compiled" ->
- get_general_info(Fd,GenInfo#general_info{compile_time=val(Fd)});
+ get_general_info(Fd,GenInfo#general_info{compile_time=bytes(Fd)});
"Taints" ->
- Val = case val(Fd) of "-1" -> "(none)"; Line -> Line end,
+ Val = case string(Fd) of "-1" -> "(none)"; Line -> Line end,
get_general_info(Fd,GenInfo#general_info{taints=Val});
"Atoms" ->
- get_general_info(Fd,GenInfo#general_info{num_atoms=val(Fd)});
+ get_general_info(Fd,GenInfo#general_info{num_atoms=bytes(Fd)});
"Calling Thread" ->
- get_general_info(Fd,GenInfo#general_info{thread=val(Fd)});
+ get_general_info(Fd,GenInfo#general_info{thread=bytes(Fd)});
"=" ++ _next_tag ->
GenInfo;
Other ->
@@ -1068,15 +1083,15 @@ get_proc_details(File,Pid,WS,DumpVsn,Binaries) ->
get_procinfo(Fd,Fun,Proc,WS) ->
case line_head(Fd) of
"State" ->
- State = case val(Fd) of
+ State = case bytes(Fd) of
"Garbing" -> "Garbing\n(limited info)";
State0 -> State0
end,
get_procinfo(Fd,Fun,Proc#proc{state=State},WS);
"Name" ->
- get_procinfo(Fd,Fun,Proc#proc{name=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{name=string(Fd)},WS);
"Spawned as" ->
- IF = val(Fd),
+ IF = string(Fd),
case Proc#proc.name of
undefined ->
get_procinfo(Fd,Fun,Proc#proc{name=IF,init_func=IF},WS);
@@ -1085,17 +1100,17 @@ get_procinfo(Fd,Fun,Proc,WS) ->
end;
"Message queue length" ->
%% stored as integer so we can sort on it
- get_procinfo(Fd,Fun,Proc#proc{msg_q_len=list_to_integer(val(Fd))},WS);
+ get_procinfo(Fd,Fun,Proc#proc{msg_q_len=list_to_integer(bytes(Fd))},WS);
"Reductions" ->
%% stored as integer so we can sort on it
- get_procinfo(Fd,Fun,Proc#proc{reds=list_to_integer(val(Fd))},WS);
+ get_procinfo(Fd,Fun,Proc#proc{reds=list_to_integer(bytes(Fd))},WS);
"Stack+heap" ->
%% stored as integer so we can sort on it
get_procinfo(Fd,Fun,Proc#proc{stack_heap=
- list_to_integer(val(Fd))*WS},WS);
+ list_to_integer(bytes(Fd))*WS},WS);
"Memory" ->
%% stored as integer so we can sort on it
- get_procinfo(Fd,Fun,Proc#proc{memory=list_to_integer(val(Fd))},WS);
+ get_procinfo(Fd,Fun,Proc#proc{memory=list_to_integer(bytes(Fd))},WS);
{eof,_} ->
Proc; % truncated file
Other ->
@@ -1117,67 +1132,67 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) ->
case LineHead of
%% - START - moved from get_procinfo -
"Spawned by" ->
- case val(Fd) of
+ case bytes(Fd) of
"[]" ->
get_procinfo(Fd,Fun,Proc,WS);
Parent ->
get_procinfo(Fd,Fun,Proc#proc{parent=Parent},WS)
end;
"Started" ->
- get_procinfo(Fd,Fun,Proc#proc{start_time=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{start_time=bytes(Fd)},WS);
"Last scheduled in for" ->
get_procinfo(Fd,Fun,Proc#proc{current_func=
{"Last scheduled in for",
- val(Fd)}},WS);
+ string(Fd)}},WS);
"Current call" ->
get_procinfo(Fd,Fun,Proc#proc{current_func={"Current call",
- val(Fd)}},WS);
+ string(Fd)}},WS);
"Number of heap fragments" ->
- get_procinfo(Fd,Fun,Proc#proc{num_heap_frag=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{num_heap_frag=bytes(Fd)},WS);
"Heap fragment data" ->
- get_procinfo(Fd,Fun,Proc#proc{heap_frag_data=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{heap_frag_data=bytes(Fd)},WS);
"OldHeap" ->
- Bytes = list_to_integer(val(Fd))*WS,
+ Bytes = list_to_integer(bytes(Fd))*WS,
get_procinfo(Fd,Fun,Proc#proc{old_heap=Bytes},WS);
"Heap unused" ->
- Bytes = list_to_integer(val(Fd))*WS,
+ Bytes = list_to_integer(bytes(Fd))*WS,
get_procinfo(Fd,Fun,Proc#proc{heap_unused=Bytes},WS);
"OldHeap unused" ->
- Bytes = list_to_integer(val(Fd))*WS,
+ Bytes = list_to_integer(bytes(Fd))*WS,
get_procinfo(Fd,Fun,Proc#proc{old_heap_unused=Bytes},WS);
"New heap start" ->
- get_procinfo(Fd,Fun,Proc#proc{new_heap_start=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{new_heap_start=bytes(Fd)},WS);
"New heap top" ->
- get_procinfo(Fd,Fun,Proc#proc{new_heap_top=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{new_heap_top=bytes(Fd)},WS);
"Stack top" ->
- get_procinfo(Fd,Fun,Proc#proc{stack_top=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{stack_top=bytes(Fd)},WS);
"Stack end" ->
- get_procinfo(Fd,Fun,Proc#proc{stack_end=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{stack_end=bytes(Fd)},WS);
"Old heap start" ->
- get_procinfo(Fd,Fun,Proc#proc{old_heap_start=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{old_heap_start=bytes(Fd)},WS);
"Old heap top" ->
- get_procinfo(Fd,Fun,Proc#proc{old_heap_top=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{old_heap_top=bytes(Fd)},WS);
"Old heap end" ->
- get_procinfo(Fd,Fun,Proc#proc{old_heap_end=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{old_heap_end=bytes(Fd)},WS);
%% - END - moved from get_procinfo -
"Last calls" ->
get_procinfo(Fd,Fun,Proc#proc{last_calls=get_lines_to_empty(Fd)},WS);
"Link list" ->
- {Links,Monitors,MonitoredBy} = parse_link_list(val(Fd),[],[],[]),
+ {Links,Monitors,MonitoredBy} = parse_link_list(bytes(Fd),[],[],[]),
get_procinfo(Fd,Fun,Proc#proc{links=Links,
monitors=Monitors,
mon_by=MonitoredBy},WS);
"Program counter" ->
- get_procinfo(Fd,Fun,Proc#proc{prog_count=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{prog_count=string(Fd)},WS);
"CP" ->
- get_procinfo(Fd,Fun,Proc#proc{cp=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{cp=string(Fd)},WS);
"arity = " ++ Arity ->
%%! Temporary workaround
get_procinfo(Fd,Fun,Proc#proc{arity=Arity--"\r\n"},WS);
"Run queue" ->
- get_procinfo(Fd,Fun,Proc#proc{run_queue=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{run_queue=string(Fd)},WS);
"Internal State" ->
- get_procinfo(Fd,Fun,Proc#proc{int_state=val(Fd)},WS);
+ get_procinfo(Fd,Fun,Proc#proc{int_state=string(Fd)},WS);
"=" ++ _next_tag ->
Proc;
Other ->
@@ -1204,7 +1219,7 @@ parse_link_list(", "++Rest,Links,Monitors,MonitoredBy) ->
parse_link_list([],Links,Monitors,MonitoredBy) ->
{lists:reverse(Links),lists:reverse(Monitors),lists:reverse(MonitoredBy)};
parse_link_list(Unexpected,Links,Monitors,MonitoredBy) ->
- io:format("WARNING: found unexpected data in link list:~n~s~n",[Unexpected]),
+ io:format("WARNING: found unexpected data in link list:~n~ts~n",[Unexpected]),
parse_link_list([],Links,Monitors,MonitoredBy).
@@ -1363,7 +1378,7 @@ read_stack_dump(Fd,Pid,BinAddrAdj,Dict) ->
end.
read_stack_dump1(Fd,BinAddrAdj,Dict,Acc) ->
%% This function is never called if the dump is truncated in {?proc_heap,Pid}
- case val(Fd) of
+ case bytes(Fd) of
"=" ++ _next_tag ->
lists:reverse(Acc);
Line ->
@@ -1391,7 +1406,7 @@ read_messages(Fd,Pid,BinAddrAdj,Dict) ->
end.
read_messages1(Fd,BinAddrAdj,Dict,Acc) ->
%% This function is never called if the dump is truncated in {?proc_heap,Pid}
- case val(Fd) of
+ case bytes(Fd) of
"=" ++ _next_tag ->
lists:reverse(Acc);
Line ->
@@ -1419,7 +1434,7 @@ read_dictionary(Fd,Pid,BinAddrAdj,Dict) ->
end.
read_dictionary1(Fd,BinAddrAdj,Dict,Acc) ->
%% This function is never called if the dump is truncated in {?proc_heap,Pid}
- case val(Fd) of
+ case bytes(Fd) of
"=" ++ _next_tag ->
lists:reverse(Acc);
Line ->
@@ -1451,7 +1466,7 @@ read_heap(BinAddrAdj,Dict0) ->
end_of_heap ->
Dict0;
Fd ->
- case val(Fd) of
+ case bytes(Fd) of
"=" ++ _next_tag ->
put(fd, end_of_heap),
Dict0;
@@ -1498,42 +1513,42 @@ get_portinfo(Fd,Port) ->
case line_head(Fd) of
"Slot" ->
%% stored as integer so we can sort on it
- get_portinfo(Fd,Port#port{slot=list_to_integer(val(Fd))});
+ get_portinfo(Fd,Port#port{slot=list_to_integer(bytes(Fd))});
"Connected" ->
%% stored as pid so we can sort on it
- Connected0 = val(Fd),
+ Connected0 = bytes(Fd),
Connected =
try list_to_pid(Connected0)
catch error:badarg -> Connected0
end,
get_portinfo(Fd,Port#port{connected=Connected});
"Links" ->
- Pids = split_pid_list_no_space(val(Fd)),
+ Pids = split_pid_list_no_space(bytes(Fd)),
Links = [{Pid,Pid} || Pid <- Pids],
get_portinfo(Fd,Port#port{links=Links});
"Registered as" ->
- get_portinfo(Fd,Port#port{name=val(Fd)});
+ get_portinfo(Fd,Port#port{name=string(Fd)});
"Monitors" ->
- Monitors0 = string:tokens(val(Fd),"()"),
+ Monitors0 = string:tokens(bytes(Fd),"()"),
Monitors = [begin
[Pid,Ref] = string:tokens(Mon,","),
{Pid,Pid++" ("++Ref++")"}
end || Mon <- Monitors0],
get_portinfo(Fd,Port#port{monitors=Monitors});
"Port controls linked-in driver" ->
- Str = lists:flatten(["Linked in driver: " | val(Fd)]),
+ Str = lists:flatten(["Linked in driver: " | string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
"Port controls forker process" ->
- Str = lists:flatten(["Forker process: " | val(Fd)]),
+ Str = lists:flatten(["Forker process: " | string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
"Port controls external process" ->
- Str = lists:flatten(["External proc: " | val(Fd)]),
+ Str = lists:flatten(["External proc: " | string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
"Port is a file" ->
- Str = lists:flatten(["File: "| val(Fd)]),
+ Str = lists:flatten(["File: "| string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
"Port is UNIX fd not opened by emulator" ->
- Str = lists:flatten(["UNIX fd not opened by emulator: "| val(Fd)]),
+ Str = lists:flatten(["UNIX fd not opened by emulator: "| string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
"=" ++ _next_tag ->
Port;
@@ -1566,23 +1581,23 @@ tab_is_named(#ets_table{}) -> "no".
get_etsinfo(Fd,EtsTable = #ets_table{details=Ds},WS) ->
case line_head(Fd) of
"Slot" ->
- get_etsinfo(Fd,EtsTable#ets_table{slot=list_to_integer(val(Fd))},WS);
+ get_etsinfo(Fd,EtsTable#ets_table{slot=list_to_integer(bytes(Fd))},WS);
"Table" ->
- get_etsinfo(Fd,EtsTable#ets_table{id=val(Fd)},WS);
+ get_etsinfo(Fd,EtsTable#ets_table{id=string(Fd)},WS);
"Name" ->
- get_etsinfo(Fd,EtsTable#ets_table{name=val(Fd)},WS);
+ get_etsinfo(Fd,EtsTable#ets_table{name=string(Fd)},WS);
"Ordered set (AVL tree), Elements" ->
skip_rest_of_line(Fd),
get_etsinfo(Fd,EtsTable#ets_table{data_type="tree"},WS);
"Buckets" ->
%% A bug in erl_db_hash.c prints a space after the buckets
%% - need to strip the string to make list_to_integer/1 happy.
- Buckets = list_to_integer(string:strip(val(Fd))),
+ Buckets = list_to_integer(string:strip(bytes(Fd))),
get_etsinfo(Fd,EtsTable#ets_table{buckets=Buckets},WS);
"Objects" ->
- get_etsinfo(Fd,EtsTable#ets_table{size=list_to_integer(val(Fd))},WS);
+ get_etsinfo(Fd,EtsTable#ets_table{size=list_to_integer(bytes(Fd))},WS);
"Words" ->
- Words = list_to_integer(val(Fd)),
+ Words = list_to_integer(bytes(Fd)),
Bytes =
case Words of
-1 -> -1; % probably truncated
@@ -1592,37 +1607,37 @@ get_etsinfo(Fd,EtsTable = #ets_table{details=Ds},WS) ->
"=" ++ _next_tag ->
EtsTable;
"Chain Length Min" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_min=>Val}},WS);
"Chain Length Avg" ->
- Val = try list_to_float(string:strip(val(Fd))) catch _:_ -> "-" end,
+ Val = try list_to_float(string:strip(bytes(Fd))) catch _:_ -> "-" end,
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_avg=>Val}},WS);
"Chain Length Max" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_max=>Val}},WS);
"Chain Length Std Dev" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_stddev=>Val}},WS);
"Chain Length Expected Std Dev" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{chain_exp_stddev=>Val}},WS);
"Fixed" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{fixed=>Val}},WS);
"Type" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{data_type=Val},WS);
"Protection" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{protection=>Val}},WS);
"Compressed" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{compressed=>Val}},WS);
"Write Concurrency" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{write_c=>Val}},WS);
"Read Concurrency" ->
- Val = val(Fd),
+ Val = bytes(Fd),
get_etsinfo(Fd,EtsTable#ets_table{details=Ds#{read_c=>Val}},WS);
Other ->
unexpected(Fd,Other,"ETS info"),
@@ -1672,9 +1687,9 @@ get_timerinfo(Fd,Id) ->
get_timerinfo_1(Fd,Timer) ->
case line_head(Fd) of
"Message" ->
- get_timerinfo_1(Fd,Timer#timer{msg=val(Fd)});
+ get_timerinfo_1(Fd,Timer#timer{msg=string(Fd)});
"Time left" ->
- TimeLeft = list_to_integer(val(Fd) -- " ms"),
+ TimeLeft = list_to_integer(bytes(Fd) -- " ms"),
get_timerinfo_1(Fd,Timer#timer{time=TimeLeft});
"=" ++ _next_tag ->
Timer;
@@ -1743,37 +1758,37 @@ get_nodeinfo(Fd,Channel,Type,Start) ->
get_nodeinfo(Fd,Nod) ->
case line_head(Fd) of
"Name" ->
- get_nodeinfo(Fd,Nod#nod{name=val(Fd)});
+ get_nodeinfo(Fd,Nod#nod{name=bytes(Fd)});
"Controller" ->
- get_nodeinfo(Fd,Nod#nod{controller=val(Fd)});
+ get_nodeinfo(Fd,Nod#nod{controller=bytes(Fd)});
"Creation" ->
%% Throwing away elements like "(refc=1)", which might be
%% printed from a debug compiled emulator.
Creations = lists:flatmap(fun(C) -> try [list_to_integer(C)]
catch error:badarg -> []
end
- end, string:tokens(val(Fd)," ")),
+ end, string:tokens(bytes(Fd)," ")),
get_nodeinfo(Fd,Nod#nod{creation={creations,Creations}});
"Remote link" ->
- Procs = val(Fd), % e.g. "<0.31.0> <4322.54.0>"
+ Procs = bytes(Fd), % e.g. "<0.31.0> <4322.54.0>"
{Local,Remote} = split(Procs),
Str = Local++" <-> "++Remote,
NewRemLinks = [{Local,Str} | Nod#nod.remote_links],
get_nodeinfo(Fd,Nod#nod{remote_links=NewRemLinks});
"Remote monitoring" ->
- Procs = val(Fd), % e.g. "<0.31.0> <4322.54.0>"
+ Procs = bytes(Fd), % e.g. "<0.31.0> <4322.54.0>"
{Local,Remote} = split(Procs),
Str = Local++" -> "++Remote,
NewRemMon = [{Local,Str} | Nod#nod.remote_mon],
get_nodeinfo(Fd,Nod#nod{remote_mon=NewRemMon});
"Remotely monitored by" ->
- Procs = val(Fd), % e.g. "<0.31.0> <4322.54.0>"
+ Procs = bytes(Fd), % e.g. "<0.31.0> <4322.54.0>"
{Local,Remote} = split(Procs),
Str = Local++" <- "++Remote,
NewRemMonBy = [{Local,Str} | Nod#nod.remote_mon_by],
get_nodeinfo(Fd,Nod#nod{remote_mon_by=NewRemMonBy});
"Error" ->
- get_nodeinfo(Fd,Nod#nod{error="ERROR: "++val(Fd)});
+ get_nodeinfo(Fd,Nod#nod{error="ERROR: "++string(Fd)});
"=" ++ _next_tag ->
Nod;
Other ->
@@ -1817,9 +1832,9 @@ loaded_mods(File) ->
get_loaded_mod_totals(Fd,{CC,OC}) ->
case line_head(Fd) of
"Current code" ->
- get_loaded_mod_totals(Fd,{val(Fd),OC});
+ get_loaded_mod_totals(Fd,{bytes(Fd),OC});
"Old code" ->
- get_loaded_mod_totals(Fd,{CC,val(Fd)});
+ get_loaded_mod_totals(Fd,{CC,bytes(Fd)});
"=" ++ _next_tag ->
{CC,OC};
Other ->
@@ -1830,10 +1845,10 @@ get_loaded_mod_totals(Fd,{CC,OC}) ->
get_loaded_mod_info(Fd,LM,Fun) ->
case line_head(Fd) of
"Current size" ->
- CS = list_to_integer(val(Fd)),
+ CS = list_to_integer(bytes(Fd)),
get_loaded_mod_info(Fd,LM#loaded_mod{current_size=CS},Fun);
"Old size" ->
- OS = list_to_integer(val(Fd)),
+ OS = list_to_integer(bytes(Fd)),
get_loaded_mod_info(Fd,LM#loaded_mod{old_size=OS},Fun);
"=" ++ _next_tag ->
LM;
@@ -1849,16 +1864,16 @@ main_modinfo(_Fd,LM,_LineHead) ->
all_modinfo(Fd,LM,LineHead) ->
case LineHead of
"Current attributes" ->
- Str = hex_to_str(val(Fd,"")),
+ Str = hex_to_str(bytes(Fd,"")),
LM#loaded_mod{current_attrib=Str};
"Current compilation info" ->
- Str = hex_to_str(val(Fd,"")),
+ Str = hex_to_str(bytes(Fd,"")),
LM#loaded_mod{current_comp_info=Str};
"Old attributes" ->
- Str = hex_to_str(val(Fd,"")),
+ Str = hex_to_str(bytes(Fd,"")),
LM#loaded_mod{old_attrib=Str};
"Old compilation info" ->
- Str = hex_to_str(val(Fd,"")),
+ Str = hex_to_str(bytes(Fd,"")),
LM#loaded_mod{old_comp_info=Str};
Other ->
unexpected(Fd,Other,"loaded modules info"),
@@ -1868,7 +1883,7 @@ all_modinfo(Fd,LM,LineHead) ->
hex_to_str(Hex) ->
Term = hex_to_term(Hex,[]),
- io_lib:format("~p~n",[Term]).
+ io_lib:format("~tp~n",[Term]).
hex_to_term([X,Y|Hex],Acc) ->
MS = hex_to_dec([X]),
@@ -1909,17 +1924,17 @@ funs(File) ->
get_funinfo(Fd,Fu) ->
case line_head(Fd) of
"Module" ->
- get_funinfo(Fd,Fu#fu{module=val(Fd)});
+ get_funinfo(Fd,Fu#fu{module=bytes(Fd)});
"Uniq" ->
- get_funinfo(Fd,Fu#fu{uniq=list_to_integer(val(Fd))});
+ get_funinfo(Fd,Fu#fu{uniq=list_to_integer(bytes(Fd))});
"Index" ->
- get_funinfo(Fd,Fu#fu{index=list_to_integer(val(Fd))});
+ get_funinfo(Fd,Fu#fu{index=list_to_integer(bytes(Fd))});
"Address" ->
- get_funinfo(Fd,Fu#fu{address=val(Fd)});
+ get_funinfo(Fd,Fu#fu{address=bytes(Fd)});
"Native_address" ->
- get_funinfo(Fd,Fu#fu{native_address=val(Fd)});
+ get_funinfo(Fd,Fu#fu{native_address=bytes(Fd)});
"Refc" ->
- get_funinfo(Fd,Fu#fu{refc=list_to_integer(val(Fd))});
+ get_funinfo(Fd,Fu#fu{refc=list_to_integer(bytes(Fd))});
"=" ++ _next_tag ->
Fu;
Other ->
@@ -1999,7 +2014,7 @@ get_meminfo(Fd,Acc) ->
{eof,_last_line} ->
lists:reverse(Acc);
Key ->
- get_meminfo(Fd,[{list_to_atom(Key),val(Fd)}|Acc])
+ get_meminfo(Fd,[{list_to_atom(Key),bytes(Fd)}|Acc])
end.
%%-----------------------------------------------------------------
@@ -2023,7 +2038,7 @@ get_allocareainfo(Fd,Acc) ->
{eof,_last_line} ->
lists:reverse(Acc);
Key ->
- Val = val(Fd),
+ Val = bytes(Fd),
AllocInfo =
case split(Val) of
{Alloc,[]} ->
@@ -2061,7 +2076,7 @@ get_allocatorinfo1(Fd,Acc,Max) ->
{eof,_last_line} ->
pad_and_reverse(Acc,Max,[]);
Key ->
- Values = get_all_vals(val(Fd),[]),
+ Values = get_all_vals(bytes(Fd),[]),
L = length(Values),
Max1 = if L > Max -> L; true -> Max end,
get_allocatorinfo1(Fd,[{Key,Values}|Acc],Max1)
@@ -2316,13 +2331,13 @@ get_hashtableinfo(Fd,Name,Start) ->
get_hashtableinfo1(Fd,HashTable) ->
case line_head(Fd) of
"size" ->
- get_hashtableinfo1(Fd,HashTable#hash_table{size=val(Fd)});
+ get_hashtableinfo1(Fd,HashTable#hash_table{size=bytes(Fd)});
"used" ->
- get_hashtableinfo1(Fd,HashTable#hash_table{used=val(Fd)});
+ get_hashtableinfo1(Fd,HashTable#hash_table{used=bytes(Fd)});
"objs" ->
- get_hashtableinfo1(Fd,HashTable#hash_table{objs=val(Fd)});
+ get_hashtableinfo1(Fd,HashTable#hash_table{objs=bytes(Fd)});
"depth" ->
- get_hashtableinfo1(Fd,HashTable#hash_table{depth=val(Fd)});
+ get_hashtableinfo1(Fd,HashTable#hash_table{depth=bytes(Fd)});
"=" ++ _next_tag ->
HashTable;
Other ->
@@ -2353,15 +2368,15 @@ get_indextableinfo(Fd,Name,Start) ->
get_indextableinfo1(Fd,IndexTable) ->
case line_head(Fd) of
"size" ->
- get_indextableinfo1(Fd,IndexTable#index_table{size=val(Fd)});
+ get_indextableinfo1(Fd,IndexTable#index_table{size=bytes(Fd)});
"used" ->
- get_indextableinfo1(Fd,IndexTable#index_table{used=val(Fd)});
+ get_indextableinfo1(Fd,IndexTable#index_table{used=bytes(Fd)});
"limit" ->
- get_indextableinfo1(Fd,IndexTable#index_table{limit=val(Fd)});
+ get_indextableinfo1(Fd,IndexTable#index_table{limit=bytes(Fd)});
"rate" ->
- get_indextableinfo1(Fd,IndexTable#index_table{rate=val(Fd)});
+ get_indextableinfo1(Fd,IndexTable#index_table{rate=bytes(Fd)});
"entries" ->
- get_indextableinfo1(Fd,IndexTable#index_table{entries=val(Fd)});
+ get_indextableinfo1(Fd,IndexTable#index_table{entries=bytes(Fd)});
"=" ++ _next_tag ->
IndexTable;
Other ->
@@ -2393,45 +2408,45 @@ get_schedulerinfo(Fd,Name,Start) ->
get_schedulerinfo1(Fd,Sched=#sched{details=Ds}) ->
case line_head(Fd) of
"Current Process" ->
- get_schedulerinfo1(Fd,Sched#sched{process=val(Fd, "None")});
+ get_schedulerinfo1(Fd,Sched#sched{process=bytes(Fd, "None")});
"Current Port" ->
- get_schedulerinfo1(Fd,Sched#sched{port=val(Fd, "None")});
+ get_schedulerinfo1(Fd,Sched#sched{port=bytes(Fd, "None")});
"Run Queue Max Length" ->
- RQMax = list_to_integer(val(Fd)),
+ RQMax = list_to_integer(bytes(Fd)),
RQ = RQMax + Sched#sched.run_q,
get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}});
"Run Queue High Length" ->
- RQHigh = list_to_integer(val(Fd)),
+ RQHigh = list_to_integer(bytes(Fd)),
RQ = RQHigh + Sched#sched.run_q,
get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}});
"Run Queue Normal Length" ->
- RQNorm = list_to_integer(val(Fd)),
+ RQNorm = list_to_integer(bytes(Fd)),
RQ = RQNorm + Sched#sched.run_q,
get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}});
"Run Queue Low Length" ->
- RQLow = list_to_integer(val(Fd)),
+ RQLow = list_to_integer(bytes(Fd)),
RQ = RQLow + Sched#sched.run_q,
get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}});
"Run Queue Port Length" ->
- RQ = list_to_integer(val(Fd)),
+ RQ = list_to_integer(bytes(Fd)),
get_schedulerinfo1(Fd,Sched#sched{port_q=RQ});
"Scheduler Sleep Info Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_info=>val(Fd, "None")}});
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}});
"Scheduler Sleep Info Aux Work" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_aux=>val(Fd, "None")}});
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}});
"Run Queue Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{runq_flags=>val(Fd, "None")}});
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}});
"Current Process State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_state=>val(Fd)}});
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_state=>bytes(Fd)}});
"Current Process Internal State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_int_state=>val(Fd)}});
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}});
"Current Process Program counter" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_prg_cnt=>val(Fd)}});
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}});
"Current Process CP" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_cp=>val(Fd)}});
+ get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_cp=>string(Fd)}});
"Current Process Limited Stack Trace" ->
%% If there shall be last in scheduler information block
Sched#sched{details=get_limited_stack(Fd, 0, Ds)};
@@ -2443,7 +2458,7 @@ get_schedulerinfo1(Fd,Sched=#sched{details=Ds}) ->
end.
get_limited_stack(Fd, N, Ds) ->
- case val(Fd) of
+ case string(Fd) of
Addr = "0x" ++ _ ->
get_limited_stack(Fd, N+1, Ds#{{currp_stack, N} => Addr});
"=" ++ _next_tag ->
@@ -2595,7 +2610,7 @@ skip_dist_ext([C|Cs], KeptCs) ->
parse_atom([$A|Line0], D) ->
{N,":"++Line1} = get_hex(Line0),
{Chars, Line} = get_chars(N, Line1),
- {list_to_atom(Chars), Line, D}.
+ {binary_to_atom(list_to_binary(Chars),utf8), Line, D}.
parse_atom_translation_table(0, Line0, As) ->
{list_to_tuple(lists:reverse(As)), Line0};
@@ -2614,7 +2629,7 @@ deref_ptr(Ptr, Line, BinAddrAdj, D0) ->
end_of_heap ->
{['#CDVIncompleteHeap'],Line,D0};
Fd ->
- case val(Fd) of
+ case bytes(Fd) of
"="++_ ->
put(fd, end_of_heap),
deref_ptr(Ptr, Line, BinAddrAdj, D0);
@@ -2759,7 +2774,7 @@ tag_to_atom("scheduler") -> ?scheduler;
tag_to_atom("timer") -> ?timer;
tag_to_atom("visible_node") -> ?visible_node;
tag_to_atom(UnknownTag) ->
- io:format("WARNING: Found unexpected tag:~s~n",[UnknownTag]),
+ io:format("WARNING: Found unexpected tag:~ts~n",[UnknownTag]),
list_to_atom(UnknownTag).
%%%-----------------------------------------------------------------
diff --git a/lib/observer/src/etop_tr.erl b/lib/observer/src/etop_tr.erl
index 8e43f8bb35..1e48fefca4 100644
--- a/lib/observer/src/etop_tr.erl
+++ b/lib/observer/src/etop_tr.erl
@@ -89,14 +89,14 @@ handle_data(Last, {_, Pid, out, _, Time2} = G, Store) ->
end,
New;
false ->
- io:format("Erlang top got garbage ~p~n", [G]),
+ io:format("Erlang top got garbage ~tp~n", [G]),
Last
end;
handle_data(_W, {drop, D}, _) -> %% Error case we are missing data here!
io:format("Erlang top dropped data ~p~n", [D]),
[];
handle_data(Last, G, _) ->
- io:format("Erlang top got garbage ~p~n", [G]),
+ io:format("Erlang top got garbage ~tp~n", [G]),
Last.
elapsed({Me1, S1, Mi1}, {Me2, S2, Mi2}) ->
diff --git a/lib/observer/src/etop_txt.erl b/lib/observer/src/etop_txt.erl
index 183641119a..cd3ec62c13 100644
--- a/lib/observer/src/etop_txt.erl
+++ b/lib/observer/src/etop_txt.erl
@@ -48,7 +48,6 @@ do_update(Prev,Config) ->
do_update(standard_io,Info,Prev,Config).
do_update(Fd,Info,Prev,Config) ->
- Encoding = encoding(Fd),
{Cpu,NProcs,RQ,Clock} = loadinfo(Info,Prev),
io:nl(Fd),
writedoubleline(Fd),
@@ -72,7 +71,7 @@ do_update(Fd,Info,Prev,Config) ->
io:nl(Fd),
writepinfo_header(Fd),
writesingleline(Fd),
- writepinfo(Fd,Info#etop_info.procinfo,Encoding),
+ writepinfo(Fd,Info#etop_info.procinfo,modifier(Fd)),
writedoubleline(Fd),
io:nl(Fd),
Info.
@@ -93,26 +92,27 @@ writepinfo(Fd,[#etop_proc_info{pid=Pid,
cf=MFA,
mq=MQ}
|T],
- Encoding) ->
- io:fwrite(Fd,proc_format(Encoding),
- [Pid,to_list(Name,Encoding),Time,Reds,Mem,MQ,
- formatmfa(MFA,Encoding)]),
- writepinfo(Fd,T,Encoding);
+ Modifier) ->
+ io:fwrite(Fd,proc_format(Modifier),
+ [Pid,to_string(Name,Modifier),Time,Reds,Mem,MQ,
+ to_string(MFA,Modifier)]),
+ writepinfo(Fd,T,Modifier);
writepinfo(_Fd,[],_) ->
ok.
+proc_format(Modifier) ->
+ "~-15w~-20"++Modifier++"s~8w~8w~8w~8w ~-20"++Modifier++"s~n".
-formatmfa({M, F, A},latin1) ->
- io_lib:format("~w:~w/~w",[M, F, A]);
-formatmfa({M, F, A},_) ->
- io_lib:format("~w:~tw/~w",[M, F, A]);
-formatmfa(Other,_) ->
- %% E.g. when running hipe - the current_function for some
- %% processes will be 'undefined'
- io_lib:format("~w",[Other]).
+to_string({M,F,A},Modifier) ->
+ io_lib:format("~w:~"++Modifier++"w/~w",[M,F,A]);
+to_string(Other,Modifier) ->
+ io_lib:format("~"++Modifier++"w",[Other]).
-to_list(Name,_) when is_atom(Name) -> atom_to_list(Name);
-to_list({_M,_F,_A}=MFA,Encoding) -> formatmfa(MFA,Encoding).
+modifier(Device) ->
+ case encoding(Device) of
+ latin1 -> "";
+ _ -> "t"
+ end.
encoding(Device) ->
case io:getopts(Device) of
@@ -122,7 +122,3 @@ encoding(Device) ->
latin1
end.
-proc_format(latin1) ->
- "~-15w~-20s~8w~8w~8w~8w ~-20s~n";
-proc_format(_) ->
- "~-15w~-20ts~8w~8w~8w~8w ~-20ts~n".
diff --git a/lib/observer/src/multitrace.erl b/lib/observer/src/multitrace.erl
index a01eeec6ae..82aec05e0d 100644
--- a/lib/observer/src/multitrace.erl
+++ b/lib/observer/src/multitrace.erl
@@ -103,16 +103,16 @@ print_func(Out,{trace_ts,P,call,{M,F,A},C,Ts},N) ->
io:format(Out,
"~w: ~s~n"
"Process : ~w~n"
- "Call : ~w:~w/~w~n"
- "Arguments : ~p~n"
- "Caller : ~w~n~n",
+ "Call : ~w:~tw/~w~n"
+ "Arguments : ~tp~n"
+ "Caller : ~tw~n~n",
[N,ts(Ts),P,M,F,length(A),A,C]);
print_func(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) ->
io:format(Out,
"~w: ~s~n"
"Process : ~w~n"
- "Return from : ~w:~w/~w~n"
- "Return value : ~p~n~n",
+ "Return from : ~w:~tw/~w~n"
+ "Return value : ~tp~n~n",
[N,ts(Ts),P,M,F,A,R]).
@@ -181,7 +181,7 @@ handle_schedule(Out,{trace_ts,P,out,Info,Ts},_TI,S) ->
"out:~n"
"Process : ~w~n"
"Time : ~s~n"
- "Function : ~w~n~n",[P,ts(Ts),Info]),
+ "Function : ~tw~n~n",[P,ts(Ts),Info]),
case lists:keysearch(P,1,S) of
{value,{P,List}} ->
lists:keyreplace(P,1,S,{P,[{out,Ts}|List]});
@@ -193,7 +193,7 @@ handle_schedule(Out,{trace_ts,P,in,Info,Ts},_TI,S) ->
"in:~n"
"Process : ~w~n"
"Time : ~s~n"
- "Function : ~w~n~n",[P,ts(Ts),Info]),
+ "Function : ~tw~n~n",[P,ts(Ts),Info]),
case lists:keysearch(P,1,S) of
{value,{P,List}} ->
lists:keyreplace(P,1,S,{P,[{in,Ts}|List]});
diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl
index ef425f0874..7f4b3dd484 100644
--- a/lib/observer/src/observer_alloc_wx.erl
+++ b/lib/observer/src/observer_alloc_wx.erl
@@ -80,7 +80,7 @@ init([Notebook, Parent, Config]) ->
}
}
catch _:Err ->
- io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]),
+ io:format("~p crashed ~tp: ~tp~n",[?MODULE, Err, erlang:get_stacktrace()]),
{stop, Err}
end.
@@ -183,7 +183,7 @@ handle_info({'EXIT', Old, _}, State = #state{appmon=Old}) ->
{noreply, State#state{active=false, appmon=undefined}};
handle_info(_Event, State) ->
- %% io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]),
+ %% io:format("~p:~p: ~tp~n",[?MODULE,?LINE,_Event]),
{noreply, State}.
terminate(_Event, #state{}) ->
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index bc4f1fe117..2a481966da 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -320,7 +320,7 @@ handle_info({'EXIT', _, noconnection}, State) ->
handle_info({'EXIT', _, normal}, State) ->
{noreply, State};
handle_info(_Event, State) ->
- %% io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]),
+ %% io:format("~p:~p: ~tp~n",[?MODULE,?LINE,_Event]),
{noreply, State}.
%%%%%%%%%%
diff --git a/lib/observer/src/observer_html_lib.erl b/lib/observer/src/observer_html_lib.erl
index 1f1306c370..3dfcc42ada 100644
--- a/lib/observer/src/observer_html_lib.erl
+++ b/lib/observer/src/observer_html_lib.erl
@@ -142,13 +142,13 @@ dict_table(Tab,{Key0,Value0}, Even) ->
tr(color(Even), [td("VALIGN=center",pre(Key)), td(pre(Value))]).
proc_state(Tab,{Key0,Value0}, Even) ->
- Key = lists:flatten(io_lib:format("~s",[Key0])),
+ Key = lists:flatten(io_lib:format("~ts",[Key0])),
Value = all_or_expand(Tab,Value0),
tr(color(Even), [td("VALIGN=center",Key), td(pre(Value))]).
all_or_expand(Tab,Term) ->
- Preview = io_lib:format("~P",[Term,8]),
- Check = io_lib:format("~P",[Term,100]),
+ Preview = io_lib:format("~tP",[Term,8]),
+ Check = io_lib:format("~tP",[Term,100]),
Exp = Preview=/=Check,
all_or_expand(Tab,Term,Preview,Exp).
all_or_expand(_Tab,Term,Str,false)
@@ -166,13 +166,8 @@ all_or_expand(Tab,Term,Preview,true)
"Click to expand above term")];
all_or_expand(Tab,Bin,_PreviewStr,_Expand)
when is_binary(Bin) ->
- Size = byte_size(Bin),
- PrevSize = min(Size, 10) * 8,
- <<Preview:PrevSize, _/binary>> = Bin,
- Hash = erlang:phash2(Bin),
- Key = {Preview, Size, Hash},
- ets:insert(Tab,{Key,Bin}),
- Term = io_lib:format("~p", [['#OBSBin',Preview,Size,Hash]]),
+ OBSBin = observer_lib:make_obsbin(Bin,Tab),
+ Term = io_lib:format("~tp", [OBSBin]),
href_proc_port(lists:flatten(Term), true).
color(true) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_EVEN));
@@ -339,9 +334,11 @@ href_proc_bin(From, T, Acc, LTB) ->
BinStr =
case string:tokens(OffsetSizePos,",.| \n") of
[Offset,SizeStr,Pos] when From =:= cdv ->
- Id = {list_to_integer(Offset),10,list_to_integer(Pos)},
+ Size = list_to_integer(SizeStr),
+ PreviewSize = min(Size,10),
+ Id = {list_to_integer(Offset),PreviewSize,list_to_integer(Pos)},
{ok,PreviewBin} = crashdump_viewer:expand_binary(Id),
- PreviewStr = preview_string(list_to_integer(SizeStr), PreviewBin),
+ PreviewStr = preview_string(Size, PreviewBin),
if LTB ->
href("TARGET=\"expanded\"",
["#Binary?offset="++Offset++
@@ -351,14 +348,14 @@ href_proc_bin(From, T, Acc, LTB) ->
true ->
PreviewStr
end;
- [Preview,SizeStr,Md5] when From =:= obs ->
+ [PreviewIntStr,SizeStr,Md5] when From =:= obs ->
Size = list_to_integer(SizeStr),
- PrevSize = min(Size, 10) * 8,
- PreviewStr = preview_string(Size,
- <<(list_to_integer(Preview)):PrevSize>>),
+ PreviewInt = list_to_integer(PreviewIntStr),
+ PrevSize = (trunc(math:log2(PreviewInt)/8)+1)*8,
+ PreviewStr = preview_string(Size,<<PreviewInt:PrevSize>>),
if LTB ->
href("TARGET=\"expanded\"",
- ["#OBSBinary?key1="++Preview++
+ ["#OBSBinary?key1="++PreviewIntStr++
"&key2="++SizeStr++
"&key3="++Md5],
PreviewStr);
@@ -372,14 +369,14 @@ href_proc_bin(From, T, Acc, LTB) ->
preview_string(Size, PreviewBin) when Size > 10 ->
["&lt;&lt;",
- remove_lgt(io_lib:format("~p",[PreviewBin])),
+ remove_lgt(io_lib:format("~tp",[PreviewBin])),
"...(",
observer_lib:to_str({bytes,Size}),
")",
"&gt;&gt"];
preview_string(_, PreviewBin) ->
["&lt;&lt;",
- remove_lgt(io_lib:format("~p",[PreviewBin])),
+ remove_lgt(io_lib:format("~tp",[PreviewBin])),
"&gt;&gt"].
remove_lgt(Deep) ->
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 8b6036f52a..463fb5b8ef 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -30,7 +30,8 @@
create_attrs/0,
set_listctrl_col_size/2,
create_status_bar/1,
- html_window/1, html_window/2
+ html_window/1, html_window/2,
+ make_obsbin/2
]).
-include_lib("wx/include/wx.hrl").
@@ -300,7 +301,7 @@ to_str(Float) when is_float(Float) ->
to_str({trunc, Float}) when is_float(Float) ->
float_to_list(Float, [{decimals,0}]);
to_str(Term) ->
- io_lib:format("~w", [Term]).
+ io_lib:format("~tw", [Term]).
create_menus([], _MenuBar, _Type) -> ok;
create_menus(Menus, MenuBar, Type) ->
@@ -520,7 +521,7 @@ link_entry2(Panel,{Target,Str},Cursor) ->
TC.
to_link(RegName={Name, Node}) when is_atom(Name), is_atom(Node) ->
- Str = io_lib:format("{~p,~p}", [Name, Node]),
+ Str = io_lib:format("{~tp,~p}", [Name, Node]),
{RegName, Str};
to_link(TI = {_Target, _Identifier}) ->
TI;
@@ -641,11 +642,11 @@ parse_string(Str) ->
Tokens = case erl_scan:string(Str, 1, [text]) of
{ok, Ts, _} -> Ts;
{error, {_SLine, SMod, SError}, _} ->
- throw(io_lib:format("~s", [SMod:format_error(SError)]))
+ throw(io_lib:format("~ts", [SMod:format_error(SError)]))
end,
case lib:extended_parse_term(Tokens) of
{error, {_PLine, PMod, PError}} ->
- throw(io_lib:format("~s", [PMod:format_error(PError)]));
+ throw(io_lib:format("~ts", [PMod:format_error(PError)]));
Res -> Res
end
catch
@@ -769,3 +770,26 @@ update_progress_text(PD,Text) ->
end.
finish_progress(PD) ->
wxProgressDialog:destroy(PD).
+
+make_obsbin(Bin,Tab) ->
+ Size = byte_size(Bin),
+ Preview =
+ try
+ %% The binary might be a unicode string, in which case we
+ %% don't want to split it in the middle of a grapheme
+ %% cluster - thus trying string:length and slice.
+ PL1 = min(string:length(Bin), 10),
+ PB1 = string:slice(Bin,0,PL1),
+ PS1 = byte_size(PB1) * 8,
+ <<P1:PS1>> = PB1,
+ P1
+ catch _:_ ->
+ %% Probably not a string, so just split anywhere
+ PS2 = min(Size, 10) * 8,
+ <<P2:PS2, _/binary>> = Bin,
+ P2
+ end,
+ Hash = erlang:phash2(Bin),
+ Key = {Preview, Size, Hash},
+ ets:insert(Tab, {Key,Bin}),
+ ['#OBSBin',Preview,Size,Hash].
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index fcc51310c8..5adfadb16e 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -87,7 +87,7 @@ init([Notebook, Parent, Config]) ->
},
{Panel, State0}
catch _:Err ->
- io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]),
+ io:format("~p crashed ~tp: ~tp~n",[?MODULE, Err, erlang:get_stacktrace()]),
{stop, Err}
end.
@@ -235,7 +235,7 @@ handle_info({'EXIT', Old, _}, State = #state{appmon=Old}) ->
{noreply, State#state{active=false, appmon=undefined}};
handle_info(_Event, State) ->
- %% io:format("~p:~p: ~p~n",[?MODULE,?LINE,_Event]),
+ %% io:format("~p:~p: ~tp~n",[?MODULE,?LINE,_Event]),
{noreply, State}.
%%%%%%%%%%
diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl
index 8339267659..5908e99e36 100644
--- a/lib/observer/src/observer_port_wx.erl
+++ b/lib/observer/src/observer_port_wx.erl
@@ -338,7 +338,7 @@ handle_info({info, {port_info_not_available,NodeName}},
{noreply, State};
handle_info({error, Error}, #state{panel=Panel} = State) ->
- Str = io_lib:format("ERROR: ~s~n",[Error]),
+ Str = io_lib:format("ERROR: ~ts~n",[Error]),
observer_lib:display_info_dialog(Panel, Str),
{noreply, State};
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index 3083297f31..2e5fe0bc1a 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -217,7 +217,7 @@ call(Holder, What) ->
erlang:demonitor(Ref),
Res
after 2000 ->
- io:format("Hanging call ~p~n",[What]),
+ io:format("Hanging call ~tp~n",[What]),
""
end.
@@ -256,7 +256,7 @@ handle_info(not_active, #state{timer=Timer0}=State) ->
{noreply, State#state{timer=Timer}};
handle_info(Info, State) ->
- io:format("~p:~p, Unexpected info: ~p~n", [?MODULE, ?LINE, Info]),
+ io:format("~p:~p, Unexpected info: ~tp~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
terminate(_Reason, #state{holder=Holder}) ->
@@ -273,11 +273,11 @@ handle_call(get_config, _, #state{holder=Holder, timer=Timer}=State) ->
{reply, Conf#{acc=>Accum}, State};
handle_call(Msg, _From, State) ->
- io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p:~p: Unhandled call ~tp~n",[?MODULE, ?LINE, Msg]),
{reply, ok, State}.
handle_cast(Msg, State) ->
- io:format("~p:~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]),
+ io:format("~p:~p: Unhandled cast ~tp~n", [?MODULE, ?LINE, Msg]),
{noreply, State}.
%%%%%%%%%%%%%%%%%%%%LOOP%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -401,7 +401,7 @@ handle_event(#wx{event=#wxList{type=command_list_item_activated}},
{noreply, State#state{procinfo_menu_pids=Opened2}};
handle_event(Event, State) ->
- io:format("~p:~p: handle event ~p\n", [?MODULE, ?LINE, Event]),
+ io:format("~p:~p: handle event ~tp\n", [?MODULE, ?LINE, Event]),
{noreply, State}.
@@ -559,7 +559,7 @@ table_holder(#holder{info=Info, attrs=Attrs,
%% Node crashed will be noticed soon..
table_holder(S0#holder{backend_pid=undefined});
_What ->
- %% io:format("~p: Table holder got ~p~n",[?MODULE, _What]),
+ %% io:format("~p: Table holder got ~tp~n",[?MODULE, _What]),
table_holder(S0)
end.
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 10decd8b62..963def958b 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -56,7 +56,7 @@ init([Pid, ParentFrame, Parent]) ->
Table = ets:new(observer_expand,[set,public]),
Title=case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, registered_name]) of
[] -> io_lib:format("~p",[Pid]);
- {registered_name, Registered} -> io_lib:format("~p (~p)",[Registered, Pid]);
+ {registered_name, Registered} -> io_lib:format("~tp (~p)",[Registered, Pid]);
undefined -> throw(process_undefined)
end,
Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [atom_to_list(node(Pid)), $:, Title],
@@ -171,7 +171,7 @@ handle_info({get_debug_info, From}, State = #state{notebook=Notebook}) ->
From ! {procinfo_debug, Notebook},
{noreply, State};
handle_info(_Info, State) ->
- %% io:format("~p: ~p, Handle info: ~p~n", [?MODULE, ?LINE, Info]),
+ %% io:format("~p: ~p, Handle info: ~tp~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
handle_call(Call, From, _State) ->
@@ -263,7 +263,7 @@ init_stack_page(Parent, Pid) ->
wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str({M,F,A})),
FileLine = case Info of
[{file,File},{line,Line}] ->
- io_lib:format("~s:~w", [File,Line]);
+ io_lib:format("~ts:~w", [File,Line]);
_ ->
[]
end,
@@ -487,5 +487,5 @@ io_request({put_chars, Encoding, Module, Function, Args}, State) ->
{error, {error, Function}, State}
end;
io_request(_Req, State) ->
- %% io:format("~p: Unknown req: ~p ~n",[?LINE, _Req]),
+ %% io:format("~p: Unknown req: ~tp ~n",[?LINE, _Req]),
{ok, {error, request}, State}.
diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl
index 2e1af3ada9..8c2ffd77b4 100644
--- a/lib/observer/src/observer_sys_wx.erl
+++ b/lib/observer/src/observer_sys_wx.erl
@@ -199,7 +199,7 @@ handle_info(not_active, #sys_wx_state{timer = Timer} = State) ->
{noreply, State#sys_wx_state{timer = observer_lib:stop_timer(Timer)}};
handle_info(Info, State) ->
- io:format("~p:~p: Unhandled info: ~p~n", [?MODULE, ?LINE, Info]),
+ io:format("~p:~p: Unhandled info: ~tp~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
terminate(_Reason, _State) ->
@@ -212,11 +212,11 @@ handle_call(get_config, _, #sys_wx_state{timer=Timer}=State) ->
{reply, observer_lib:timer_config(Timer), State};
handle_call(Msg, _From, State) ->
- io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p~p: Unhandled Call ~tp~n",[?MODULE, ?LINE, Msg]),
{reply, ok, State}.
handle_cast(Msg, State) ->
- io:format("~p~p: Unhandled cast ~p~n",[?MODULE, ?LINE, Msg]),
+ io:format("~p~p: Unhandled cast ~tp~n",[?MODULE, ?LINE, Msg]),
{noreply, State}.
handle_event(#wx{id = ?ID_REFRESH, event = #wxCommand{type = command_menu_selected}},
@@ -235,5 +235,5 @@ handle_event(#wx{id = ?ID_REFRESH_INTERVAL,
{noreply, State#sys_wx_state{timer=Timer}};
handle_event(Event, State) ->
- io:format("~p:~p: Unhandled event ~p\n", [?MODULE,?LINE,Event]),
+ io:format("~p:~p: Unhandled event ~tp\n", [?MODULE,?LINE,Event]),
{noreply, State}.
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index b960c61ff0..8127248262 100644
--- a/lib/observer/src/observer_trace_wx.erl
+++ b/lib/observer/src/observer_trace_wx.erl
@@ -683,7 +683,7 @@ handle_event(#wx{id=?REMOVE_NODES}, #state{n_view=Nview, nodes=Ns0} = State) ->
{noreply, State#state{nodes = Ns}};
handle_event(#wx{id=ID, event = What}, State) ->
- io:format("~p:~p: Unhandled event: ~p, ~p ~n", [?MODULE, ?LINE, ID, What]),
+ io:format("~p:~p: Unhandled event: ~p, ~tp ~n", [?MODULE, ?LINE, ID, What]),
{noreply, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -729,7 +729,7 @@ handle_info({update_ms, NewMs}, State) ->
{noreply, State#state{match_specs=NewMs}};
handle_info(Any, State) ->
- io:format("~p~p: received unexpected message: ~p\n", [?MODULE, self(), Any]),
+ io:format("~p~p: received unexpected message: ~tp\n", [?MODULE, self(), Any]),
{noreply, State}.
terminate(_Reason, #state{nodes=_Nodes}) ->
@@ -1046,33 +1046,33 @@ format_trace(Trace, Size, TS0={_,_,MS}) ->
case element(4, Trace) of
{dbg,ok} -> "";
Message ->
- io_lib:format("~s (~100p) << ~100p~n", [TS,From,Message])
+ io_lib:format("~s (~100p) << ~100tp~n", [TS,From,Message])
end;
'send' ->
Message = element(4, Trace),
To = element(5, Trace),
- io_lib:format("~s (~100p) ~100p ! ~100p~n", [TS,From,To,Message]);
+ io_lib:format("~s (~100p) ~100p ! ~100tp~n", [TS,From,To,Message]);
call ->
case element(4, Trace) of
MFA when Size == 5 ->
Message = element(5, Trace),
- io_lib:format("~s (~100p) call ~s (~100p) ~n", [TS,From,ffunc(MFA),Message]);
+ io_lib:format("~s (~100p) call ~ts (~100tp) ~n", [TS,From,ffunc(MFA),Message]);
MFA ->
- io_lib:format("~s (~100p) call ~s~n", [TS,From,ffunc(MFA)])
+ io_lib:format("~s (~100p) call ~ts~n", [TS,From,ffunc(MFA)])
end;
return_from ->
MFA = element(4, Trace),
Ret = element(5, Trace),
- io_lib:format("~s (~100p) returned from ~s -> ~100p~n", [TS,From,ffunc(MFA),Ret]);
+ io_lib:format("~s (~100p) returned from ~ts -> ~100tp~n", [TS,From,ffunc(MFA),Ret]);
return_to ->
MFA = element(4, Trace),
- io_lib:format("~s (~100p) returning to ~s~n", [TS,From,ffunc(MFA)]);
+ io_lib:format("~s (~100p) returning to ~ts~n", [TS,From,ffunc(MFA)]);
spawn when Size == 5 ->
Pid = element(4, Trace),
MFA = element(5, Trace),
- io_lib:format("~s (~100p) spawn ~100p as ~s~n", [TS,From,Pid,ffunc(MFA)]);
+ io_lib:format("~s (~100p) spawn ~100p as ~ts~n", [TS,From,Pid,ffunc(MFA)]);
Op ->
- io_lib:format("~s (~100p) ~100p ~s~n", [TS,From,Op,ftup(Trace,4,Size)])
+ io_lib:format("~s (~100p) ~100p ~ts~n", [TS,From,Op,ftup(Trace,4,Size)])
end.
%%% These f* functions returns non-flat strings
@@ -1080,24 +1080,24 @@ format_trace(Trace, Size, TS0={_,_,MS}) ->
%% {M,F,[A1, A2, ..., AN]} -> "M:F(A1, A2, ..., AN)"
%% {M,F,A} -> "M:F/A"
ffunc({M,F,Argl}) when is_list(Argl) ->
- io_lib:format("~100p:~100p(~s)", [M, F, fargs(Argl)]);
+ io_lib:format("~100p:~100tp(~ts)", [M, F, fargs(Argl)]);
ffunc({M,F,Arity}) ->
- io_lib:format("~100p:~100p/~100p", [M,F,Arity]);
-ffunc(X) -> io_lib:format("~100p", [X]).
+ io_lib:format("~100p:~100tp/~100p", [M,F,Arity]);
+ffunc(X) -> io_lib:format("~100tp", [X]).
%% Integer -> "Integer"
%% [A1, A2, ..., AN] -> "A1, A2, ..., AN"
fargs(Arity) when is_integer(Arity) -> integer_to_list(Arity);
fargs([]) -> [];
-fargs([A]) -> io_lib:format("~100p", [A]); %% last arg
-fargs([A|Args]) -> [io_lib:format("~100p,", [A]) | fargs(Args)];
-fargs(A) -> io_lib:format("~100p", [A]). % last or only arg
+fargs([A]) -> io_lib:format("~100tp", [A]); %% last arg
+fargs([A|Args]) -> [io_lib:format("~100tp,", [A]) | fargs(Args)];
+fargs(A) -> io_lib:format("~100tp", [A]). % last or only arg
%% {A_1, A_2, ..., A_N} -> "A_Index A_Index+1 ... A_Size"
ftup(Trace, Index, Index) ->
- io_lib:format("~100p", [element(Index, Trace)]);
+ io_lib:format("~100tp", [element(Index, Trace)]);
ftup(Trace, Index, Size) ->
- [io_lib:format("~100p ", [element(Index, Trace)])
+ [io_lib:format("~100tp ", [element(Index, Trace)])
| ftup(Trace, Index+1, Size)].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1125,18 +1125,19 @@ get_config(#state{def_proc_flags = ProcFlags,
write_file(Frame, Filename, Config) ->
Str =
- ["%%%\n%%% This file is generated by Observer\n",
+ ["%%% ",epp:encoding_to_string(utf8), "\n"
+ "%%%\n%%% This file is generated by Observer\n",
"%%%\n%%% DO NOT EDIT!\n%%%\n",
- [io_lib:format("~p.~n",[MSTerm]) ||
+ [io_lib:format("~tp.~n",[MSTerm]) ||
MSTerm <- proplists:get_value(match_specs, Config)],
io_lib:format("~p.~n",[lists:keyfind(procflags, 1, Config)]),
io_lib:format("~p.~n",[lists:keyfind(portflags, 1, Config)]),
- io_lib:format("~p.~n",[lists:keyfind(output, 1, Config)]),
- [io_lib:format("~p.~n",[ModuleTerm]) ||
+ io_lib:format("~tp.~n",[lists:keyfind(output, 1, Config)]),
+ [io_lib:format("~tp.~n",[ModuleTerm]) ||
ModuleTerm <- proplists:get_value(trace_p, Config)]
],
- case file:write_file(Filename, list_to_binary(Str)) of
+ case file:write_file(Filename, unicode:characters_to_binary(Str)) of
ok ->
success;
{error, Reason} ->
@@ -1200,7 +1201,7 @@ make_ms(MS) ->
make_ms(Name,Term,FunStr).
make_ms(Name, Term, FunStr) ->
- #match_spec{name=Name, term=Term, str=io_lib:format("~w", Term), func = FunStr}.
+ #match_spec{name=Name, term=Term, str=io_lib:format("~tw", Term), func = FunStr}.
parse_tp({tp, Mod, FAs}, State) ->
Patterns = [#tpattern{m=Mod,fa={F,A}, ms=make_ms(List)} ||
diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl
index 285c298c4b..4f46426cf6 100644
--- a/lib/observer/src/observer_traceoptions_wx.erl
+++ b/lib/observer/src/observer_traceoptions_wx.erl
@@ -487,7 +487,7 @@ edit_ms(TextCtrl, Label0, Parent) ->
_ -> Label0
end,
#match_spec{name=Label, term=MatchSpec,
- str=io_lib:format("~w",[MatchSpec]),
+ str=io_lib:format("~tw",[MatchSpec]),
func=Str}
catch
throw:cancel ->
@@ -511,18 +511,18 @@ ms_from_string(Str) ->
Tokens = case erl_scan:string(Str) of
{ok, Ts, _} -> Ts;
{error, {SLine, SMod, SError}, _} ->
- throw(io_lib:format("~w: ~s", [SLine,SMod:format_error(SError)]))
+ throw(io_lib:format("~w: ~ts", [SLine,SMod:format_error(SError)]))
end,
Exprs = case erl_parse:parse_exprs(Tokens) of
{ok, T} -> T;
{error, {PLine, PMod, PError}} ->
- throw(io_lib:format("~w: ~s", [PLine,PMod:format_error(PError)]))
+ throw(io_lib:format("~w: ~ts", [PLine,PMod:format_error(PError)]))
end,
Term = case Exprs of
[{'fun', _, {clauses, Clauses}}|_] ->
case ms_transform:transform_from_shell(dbg,Clauses,orddict:new()) of
{error, [{_,[{MSLine,Mod,MSInfo}]}],_} ->
- throw(io_lib:format("~w: ~p", [MSLine,Mod:format_error(MSInfo)]));
+ throw(io_lib:format("~w: ~tp", [MSLine,Mod:format_error(MSInfo)]));
{error, _} ->
throw("Could not convert fun() to match spec");
Ms ->
@@ -536,7 +536,7 @@ ms_from_string(Str) ->
{error, List} -> throw([[Error, $\n] || {_, Error} <- List])
end
catch error:_Reason ->
- %% io:format("Bad term: ~s~n ~p in ~p~n", [Str, _Reason, erlang:get_stacktrace()]),
+ %% io:format("Bad term: ~ts~n ~tp in ~tp~n", [Str, _Reason, erlang:get_stacktrace()]),
throw("Invalid term")
end.
@@ -556,7 +556,8 @@ filter_listbox_data(Input, Data, ListBox) ->
filter_listbox_data(Input, Data, ListBox, true).
filter_listbox_data(Input, Data, ListBox, AddClientData) ->
- FilteredData = [X || X = {Str, _} <- Data, re:run(Str, Input) =/= nomatch],
+ FilteredData = [X || X = {Str, _} <- Data,
+ re:run(Str, Input, [unicode]) =/= nomatch],
wxListBox:clear(ListBox),
wxListBox:appendStrings(ListBox, [Str || {Str,_} <- FilteredData]),
AddClientData andalso
@@ -648,9 +649,9 @@ parse_function_names(Choices) ->
parse_function_names([], Acc) ->
lists:reverse(Acc);
parse_function_names([{H, Term}|T], Acc) ->
- IsFun = re:run(H, ".*-fun-\\d*?-"),
- IsLc = re:run(H, ".*-lc\\$\\^\\d*?/\\d*?-\\d*?-"),
- IsLbc = re:run(H, ".*-lbc\\$\\^\\d*?/\\d*?-\\d*?-"),
+ IsFun = re:run(H, ".*-fun-\\d*?-", [unicode,ucp]),
+ IsLc = re:run(H, ".*-lc\\$\\^\\d*?/\\d*?-\\d*?-", [unicode,ucp]),
+ IsLbc = re:run(H, ".*-lbc\\$\\^\\d*?/\\d*?-\\d*?-", [unicode,ucp]),
Parsed =
if IsFun =/= nomatch -> "Fun: " ++ H;
IsLc =/= nomatch -> "List comprehension: " ++ H;
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index 789e060cfb..d6dcee2cda 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -258,7 +258,7 @@ handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}
State = #state{pid=Pid, grid=Grid, status=StatusBar}) ->
N = wxListCtrl:getItemCount(Grid),
Str = get_row(Pid, Index, all),
- wxStatusBar:setStatusText(StatusBar, io_lib:format("Objects: ~w: ~s",[N, Str])),
+ wxStatusBar:setStatusText(StatusBar, io_lib:format("Objects: ~w: ~ts",[N, Str])),
{noreply, State#state{selected=Index}};
handle_event(#wx{event=#wxList{type=command_list_item_activated, itemIndex=Index}},
@@ -278,7 +278,7 @@ handle_event(#wx{id=?ID_DELETE},
State = #state{grid=Grid, pid=Pid, status=StatusBar, selected=Index}) ->
Str = get_row(Pid, Index, all),
Pid ! {delete, Index},
- wxStatusBar:setStatusText(StatusBar, io_lib:format("Deleted object: ~s",[Str])),
+ wxStatusBar:setStatusText(StatusBar, io_lib:format("Deleted object: ~ts",[Str])),
wxListCtrl:setItemState(Grid, Index, 0, ?wxLIST_STATE_FOCUSED),
{noreply, State#state{selected=undefined}};
@@ -338,7 +338,7 @@ handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{type=command_text_enter,cmdS
Pid ! {mark_search_hit, false},
case search(Pid, Str, Pos, Dir, Case) of
false ->
- wxStatusBar:setStatusText(SB, io_lib:format("Not found (regexp): ~s",[Str])),
+ wxStatusBar:setStatusText(SB, io_lib:format("Not found (regexp): ~ts",[Str])),
Pid ! {mark_search_hit, Find#find.start},
wxListCtrl:refreshItem(Grid, Find#find.start),
{noreply, State#state{search=Search#search{find=Find#find{found=false}}}};
@@ -372,7 +372,7 @@ handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{cmdString=Str}},
Pid ! {mark_search_hit, false},
case search(Pid, Str, Cont#find.start, Dir, Case) of
false ->
- wxStatusBar:setStatusText(SB, io_lib:format("Not found (regexp): ~s",[Str])),
+ wxStatusBar:setStatusText(SB, io_lib:format("Not found (regexp): ~ts",[Str])),
{noreply, State};
Row ->
wxListCtrl:ensureVisible(Grid, Row),
@@ -395,19 +395,19 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL},
{noreply, State#state{timer=Timer}};
handle_event(_Event, State) ->
- %io:format("~p:~p, handle event ~p\n", [?MODULE, ?LINE, Event]),
+ %io:format("~p:~p, handle event ~tp\n", [?MODULE, ?LINE, Event]),
{noreply, State}.
handle_sync_event(_Event, _Obj, _State) ->
- %io:format("~p:~p, handle sync_event ~p\n", [?MODULE, ?LINE, Event]),
+ %io:format("~p:~p, handle sync_event ~tp\n", [?MODULE, ?LINE, Event]),
ok.
handle_call(_Event, _From, State) ->
- %io:format("~p:~p, handle call (~p) ~p\n", [?MODULE, ?LINE, From, Event]),
+ %io:format("~p:~p, handle call (~p) ~tp\n", [?MODULE, ?LINE, From, Event]),
{noreply, State}.
handle_cast(_Event, State) ->
- %io:format("~p:~p, handle cast ~p\n", [?MODULE, ?LINE, Event]),
+ %io:format("~p:~p, handle cast ~tp\n", [?MODULE, ?LINE, Event]),
{noreply, State}.
handle_info({no_rows, N}, State = #state{grid=Grid, status=StatusBar}) ->
@@ -433,7 +433,7 @@ handle_info(refresh_interval, State = #state{pid=Pid}) ->
handle_info({error, Error}, State = #state{frame=Frame}) ->
ErrorStr =
try io_lib:format("~ts", [Error]), Error
- catch _:_ -> io_lib:format("~p", [Error])
+ catch _:_ -> io_lib:format("~tp", [Error])
end,
Dlg = wxMessageDialog:new(Frame, ErrorStr),
wxMessageDialog:showModal(Dlg),
@@ -441,7 +441,7 @@ handle_info({error, Error}, State = #state{frame=Frame}) ->
{noreply, State};
handle_info(_Event, State) ->
- %% io:format("~p:~p, handle info ~p\n", [?MODULE, ?LINE, _Event]),
+ %% io:format("~p:~p, handle info ~tp\n", [?MODULE, ?LINE, _Event]),
{noreply, State}.
terminate(_Event, #state{pid=Pid, attrs=Attrs}) ->
@@ -554,7 +554,7 @@ table_holder(S0 = #holder{parent=Parent, pid=Pid, table=Table}) ->
edit_row(Row, Term, S0),
table_holder(S0);
What ->
- io:format("Table holder got ~p~n",[What]),
+ io:format("Table holder got ~tp~n",[What]),
Parent ! {refresh, 0, S0#holder.n-1},
table_holder(S0)
end.
@@ -641,7 +641,7 @@ search([Str, Row, Dir0, CaseSens],
true -> 1;
false -> -1
end,
- Res = case re:compile(Str, Opt) of
+ Res = case re:compile(Str, [unicode|Opt]) of
{ok, Re} -> re_search(Row, Dir, N, Re, Table);
{error, _} -> false
end,
@@ -665,7 +665,7 @@ get_row(From, Row, Col, Table) ->
[Object|_] when Col =:= all ->
From ! {self(), format(Object)};
[Object|_] when Col =:= all_multiline ->
- From ! {self(), io_lib:format("~p", [Object])};
+ From ! {self(), io_lib:format("~tp", [Object])};
[Object|_] when Col =:= term ->
From ! {self(), Object};
[Object|_] when tuple_size(Object) >= Col ->
@@ -801,7 +801,7 @@ format(Bin) when is_binary(Bin), byte_size(Bin) > 100 ->
io_lib:format("<<#Bin:~w>>", [byte_size(Bin)]);
format(Bin) when is_binary(Bin) ->
try
- true = printable_list(unicode:characters_to_list(Bin)),
+ true = io_lib:printable_list(unicode:characters_to_list(Bin)),
io_lib:format("<<\"~ts\">>", [Bin])
catch _:_ ->
io_lib:format("~w", [Bin])
@@ -809,7 +809,7 @@ format(Bin) when is_binary(Bin) ->
format(Float) when is_float(Float) ->
io_lib:format("~.3g", [Float]);
format(Term) ->
- io_lib:format("~w", [Term]).
+ io_lib:format("~tw", [Term]).
format_tuple(Tuple, I, Max) when I < Max ->
[format(element(I, Tuple)), $,|format_tuple(Tuple, I+1, Max)];
@@ -820,7 +820,7 @@ format_tuple(_Tuple, 1, 0) ->
format_list([]) -> "[]";
format_list(List) ->
- case printable_list(List) of
+ case io_lib:printable_list(List) of
true -> io_lib:format("\"~ts\"", [map_printable_list(List)]);
false -> [$[ | make_list(List)]
end.
@@ -849,26 +849,3 @@ map_printable_list([$\e|Cs]) ->
map_printable_list([]) -> [];
map_printable_list([C|Cs]) ->
[C|map_printable_list(Cs)].
-
-%% printable_list([Char]) -> bool()
-%% Return true if CharList is a list of printable characters, else
-%% false.
-
-printable_list([C|Cs]) when is_integer(C), C >= $ , C =< 255 ->
- printable_list(Cs);
-printable_list([$\n|Cs]) ->
- printable_list(Cs);
-printable_list([$\r|Cs]) ->
- printable_list(Cs);
-printable_list([$\t|Cs]) ->
- printable_list(Cs);
-printable_list([$\v|Cs]) ->
- printable_list(Cs);
-printable_list([$\b|Cs]) ->
- printable_list(Cs);
-printable_list([$\f|Cs]) ->
- printable_list(Cs);
-printable_list([$\e|Cs]) ->
- printable_list(Cs);
-printable_list([]) -> true;
-printable_list(_Other) -> false. %Everything else is false
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index 9564bdfa1c..e16f3cab6b 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -252,7 +252,7 @@ handle_info(not_active, State = #state{timer = Timer0}) ->
{noreply, State#state{timer=Timer}};
handle_info({error, Error}, #state{panel=Panel,opt=Opt}=State) ->
- Str = io_lib:format("ERROR: ~s~n",[Error]),
+ Str = io_lib:format("ERROR: ~ts~n",[Error]),
observer_lib:display_info_dialog(Panel,Str),
case Opt#opt.type of
mnesia -> wxMenuBar:check(observer_wx:get_menubar(), ?ID_ETS, true);
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 9b9e80f479..be93b1d5f1 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -459,7 +459,7 @@ handle_info({'EXIT', Pid, Reason}, State) ->
normal ->
{noreply, State};
_ ->
- io:format("Observer: Child (~s) crashed exiting: ~p ~p~n",
+ io:format("Observer: Child (~s) crashed exiting: ~p ~tp~n",
[pid2panel(Pid, State), Pid, Reason]),
{stop, normal, State}
end;
@@ -504,7 +504,7 @@ save_config(Panels) ->
File = config_file(),
case filelib:ensure_dir(File) of
ok ->
- Format = [io_lib:format("~p.~n",[Conf]) || Conf <- Configs],
+ Format = [io_lib:format("~tp.~n",[Conf]) || Conf <- Configs],
_ = file:write_file(File, Format);
_ ->
ignore
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index 09b0bc6710..940fdc9818 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -635,7 +635,7 @@ stop(Opts) when is_list(Opts) ->
ok;
{_, {stopped, _}} ->
%% Printout moved out of the ttb loop to avoid occasional deadlock
- io:format("Stored logs in ~s~n", [element(2, Result)]);
+ io:format("Stored logs in ~ts~n", [element(2, Result)]);
{_, _} ->
ok
end,
@@ -792,7 +792,7 @@ do_stop({FetchOrFormat, UserDir}, Sender, NodeInfo, SessionInfo) ->
write_config(?last_config, all),
Localhost = host(node()),
Dir = get_fetch_dir(UserDir, proplists:get_value(logfile, SessionInfo)),
- file:make_dir(Dir),
+ ok = filelib:ensure_dir(filename:join(Dir,"*")),
%% The nodes are traversed twice here because
%% the meta tracing in observer_backend must be
%% stopped before dbg is stopped, and dbg must
@@ -900,21 +900,29 @@ fetch_report(Localhost, Dir, Node, MetaFile) ->
fetch(Localhost,Dir,Node,MetaFile) ->
case (host(Node) == Localhost) orelse is_local(MetaFile) of
- true -> % same host, just move the files
+ true -> % same host, just move the files
Files = get_filenames(Node,MetaFile),
lists:foreach(
- fun(File0) ->
- Dest = filename:join(Dir,filename:basename(File0)),
- file:rename(File0, Dest)
- end,
- Files);
+ fun(File0) ->
+ Dest = filename:join(Dir,filename:basename(File0)),
+ file:rename(File0, Dest)
+ end,
+ Files);
false ->
{ok, LSock} = gen_tcp:listen(0, [binary,{packet,2},{active,false}]),
{ok,Port} = inet:port(LSock),
- rpc:cast(Node,observer_backend,ttb_fetch,
- [MetaFile,{Port,Localhost}]),
+ Enc = file:native_name_encoding(),
+ Args =
+ case rpc:call(Node,erlang,function_exported,
+ [observer_backend,ttb_fetch,3]) of
+ true ->
+ [MetaFile,{Port,Localhost},Enc];
+ false ->
+ [MetaFile,{Port,Localhost}]
+ end,
+ rpc:cast(Node,observer_backend,ttb_fetch,Args),
{ok, Sock} = gen_tcp:accept(LSock),
- receive_files(Dir,Sock,undefined),
+ receive_files(Dir,Sock,undefined,Enc),
ok = gen_tcp:close(LSock),
ok = gen_tcp:close(Sock)
end.
@@ -929,25 +937,48 @@ get_filenames(_N, {local,F,_}) ->
get_filenames(N, F) ->
rpc:call(N, observer_backend,ttb_get_filenames,[F]).
-receive_files(Dir,Sock,Fd) ->
+receive_files(Dir,Sock,Fd,Enc) ->
case gen_tcp:recv(Sock, 0) of
{ok, <<0,Bin/binary>>} ->
file:write(Fd,Bin),
- receive_files(Dir,Sock,Fd);
- {ok, <<1,Bin/binary>>} ->
- File0 = binary_to_list(Bin),
+ receive_files(Dir,Sock,Fd,Enc);
+ {ok, <<Code,Bin/binary>>} when Code==1; Code==2; Code==3 ->
+ File0 = decode_filename(Code,Bin,Enc),
File = filename:join(Dir,File0),
{ok,Fd1} = file:open(File,[raw,write]),
- receive_files(Dir,Sock,Fd1);
+ receive_files(Dir,Sock,Fd1,Enc);
{error, closed} ->
ok = file:close(Fd)
end.
+decode_filename(1,Bin,_Enc) ->
+ %% Old version of observer_backend - filename encoded with
+ %% list_to_binary
+ binary_to_list(Bin);
+decode_filename(2,Bin,Enc) ->
+ %% Successfully encoded filename with correct encoding
+ unicode:characters_to_list(Bin,Enc);
+decode_filename(3,Bin,latin1) ->
+ %% Filename encoded with faulty encoding. This has to be utf8
+ %% remote and latin1 here, and the filename actually containing
+ %% characters outside the latin1 range. So making an escaped
+ %% variant of the filename and warning about it.
+ File0 = unicode:characters_to_list(Bin,utf8),
+ File = [ case X of
+ High when High > 255 ->
+ ["\\\\x{",erlang:integer_to_list(X, 16),$}];
+ Low ->
+ Low
+ end || X <- File0 ],
+ io:format("Warning: fetching file with faulty filename encoding ~ts~n"
+ "Will be written as ~ts~n",
+ [File0,File]),
+ File.
+
host(Node) ->
[_name,Host] = string:tokens(atom_to_list(Node),"@"),
Host.
-
wait_for_fetch([]) ->
ok;
wait_for_fetch(Nodes) ->
@@ -1087,7 +1118,7 @@ read_traci(File) ->
{ok,B} ->
interpret_binary(B,dict:new(),[]);
_ ->
- io:format("Warning: no meta data file: ~s~n",[MetaFile]),
+ io:format("Warning: no meta data file: ~ts~n",[MetaFile]),
{dict:new(),[]}
end.
@@ -1303,7 +1334,7 @@ get_term(B) ->
end.
display_warning(Item,Warning) ->
- io:format("Warning: {~w,~w}~n",[Warning,Item]).
+ io:format("Warning: {~tw,~tw}~n",[Warning,Item]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/src/ttb_et.erl b/lib/observer/src/ttb_et.erl
index 95e8e9aa07..1b828eebc0 100644
--- a/lib/observer/src/ttb_et.erl
+++ b/lib/observer/src/ttb_et.erl
@@ -137,13 +137,13 @@ processes(E0) ->
E = label(E0),
{{FromProc,FromNode},{ToProc,ToNode}} =
get_actors(E#event.from,E#event.to),
- {true,E#event{from = io_lib:format("~w~n~w",[FromProc,FromNode]),
- to = io_lib:format("~w~n~w",[ToProc,ToNode])}}.
+ {true,E#event{from = io_lib:format("~tw~n~w",[FromProc,FromNode]),
+ to = io_lib:format("~tw~n~w",[ToProc,ToNode])}}.
mods_and_procs(E) ->
ActorFun = fun({M,_F,_A},{Proc,Node}) ->
- io_lib:format("~w~n~w~n~w",[M,Proc,Node])
+ io_lib:format("~w~n~tw~n~w",[M,Proc,Node])
end,
calltrace_filter(E,ActorFun).
@@ -155,13 +155,13 @@ modules(E) ->
funcs_and_procs(E) ->
ActorFun = fun({M,F,A},{Proc,Node}) ->
- io_lib:format("~s~n~w~n~w",[mfa(M,F,A),Proc,Node])
+ io_lib:format("~ts~n~tw~n~w",[mfa(M,F,A),Proc,Node])
end,
calltrace_filter(E,ActorFun).
functions(E) ->
ActorFun = fun({M,F,A},{_Proc,Node}) ->
- io_lib:format("~s~n~w",[mfa(M,F,A),Node])
+ io_lib:format("~ts~n~w",[mfa(M,F,A),Node])
end,
calltrace_filter(E,ActorFun).
@@ -221,7 +221,7 @@ label(Event=#event{label=L,contents=C}) ->
false -> Event
end.
label(L,{M,F,A}) -> label(L,M,F,A);
-label(L,Other) -> io_lib:format("~w ~w",[L,Other]).
+label(L,Other) -> io_lib:format("~w ~tw",[L,Other]).
label(call,M,F,A) -> "call " ++ mfa(M,F,A);
label(return_from,M,F,A) -> "return_from " ++ mfa(M,F,A);
label(return_to,M,F,A) -> "return_to " ++ mfa(M,F,A);
diff --git a/lib/observer/test/Makefile b/lib/observer/test/Makefile
index 6100af5e17..fcb1b73911 100644
--- a/lib/observer/test/Makefile
+++ b/lib/observer/test/Makefile
@@ -27,7 +27,8 @@ MODULES = \
ttb_SUITE \
client \
server \
- crashdump_helper
+ crashdump_helper \
+ crashdump_helper_unicode
ERL_FILES= $(MODULES:%=%.erl)
@@ -46,7 +47,7 @@ RELSYSDIR = $(RELEASE_PATH)/observer_test
# FLAGS
# ----------------------------------------------------
ERL_MAKE_FLAGS +=
-ERL_COMPILE_FLAGS +=
+ERL_COMPILE_FLAGS += +nowarn_export_all
EBIN = .
diff --git a/lib/observer/test/crashdump_helper_unicode.erl b/lib/observer/test/crashdump_helper_unicode.erl
new file mode 100644
index 0000000000..60c3d20315
--- /dev/null
+++ b/lib/observer/test/crashdump_helper_unicode.erl
@@ -0,0 +1,22 @@
+-module(crashdump_helper_unicode).
+-behaviour(gen_server).
+-export([start/0, init/1, handle_call/3, handle_cast/2]).
+-record(state, {s,a,b,lb}).
+
+start() ->
+ gen_server:start({local, 'unicode_reg_name_αβ'}, ?MODULE, [], []).
+
+init([]) ->
+ process_flag(trap_exit, true),
+ ets:new('tab_αβ',[set,named_table]),
+ Bin = <<"bin αβ"/utf8>>,
+ LongBin = <<"long bin αβ - a utf8 binary which can be expanded αβ"/utf8>>,
+ {ok, #state{s = "unicode_string_αβ",
+ a = 'unicode_atom_αβ',
+ b = Bin,
+ lb = LongBin}}.
+
+handle_call(_Info, _From, State) ->
+ {reply, ok, State}.
+handle_cast(_Info, State) ->
+ {noreply, State}.
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 1fd94ffb3c..77cf086d4b 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -427,6 +427,18 @@ special(File,Procs) ->
{ok,_,[TW]} = crashdump_viewer:general_info(),
{match,_} = re:run(TW,"CRASH DUMP SIZE LIMIT REACHED"),
ok;
+ ".unicode" ->
+ #proc{pid=Pid0} =
+ lists:keyfind("'unicode_reg_name_αβ'",#proc.name,Procs),
+ Pid = pid_to_list(Pid0),
+ {ok,#proc{},[]} = crashdump_viewer:proc_details(Pid),
+ io:format(" unicode registered name ok",[]),
+
+ {ok,[#ets_table{id="'tab_αβ'",name="'tab_αβ'"}],[]} =
+ crashdump_viewer:ets_tables(Pid),
+ io:format(" unicode table name ok",[]),
+
+ ok;
_ ->
ok
end,
@@ -492,7 +504,8 @@ do_create_dumps(DataDir,Rel) ->
CD5 = dump_with_args(DataDir,Rel,"trunc.bytes",
"-env ERL_CRASH_DUMP_BYTES " ++
integer_to_list(Bytes)),
- {[CD1,CD2,CD3,CD4,CD5], DosDump};
+ CD6 = dump_with_unicode_atoms(DataDir,Rel,"unicode"),
+ {[CD1,CD2,CD3,CD4,CD5,CD6], DosDump};
_ ->
{[CD1,CD2], DosDump}
end.
@@ -573,6 +586,16 @@ dump_with_strange_module_name(DataDir,Rel,DumpName) ->
?t:stop_node(n1),
CD.
+dump_with_unicode_atoms(DataDir,Rel,DumpName) ->
+ Opt = rel_opt(Rel),
+ Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"",
+ PzOpt = [{args,Pz}],
+ {ok,N1} = ?t:start_node(n1,peer,Opt ++ PzOpt),
+ {ok,_Pid} = rpc:call(N1,crashdump_helper_unicode,start,[]),
+ CD = dump(N1,DataDir,Rel,DumpName),
+ ?t:stop_node(n1),
+ CD.
+
dump(Node,DataDir,Rel,DumpName) ->
Crashdump = filename:join(DataDir, dump_prefix(Rel)++DumpName),
rpc:call(Node,os,putenv,["ERL_CRASH_DUMP",Crashdump]),
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index 7f0c1ac6e4..1b075a507d 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -36,6 +36,7 @@
ttb_write_binary/2,
ttb_stop/1,
ttb_fetch/2,
+ ttb_fetch/3,
ttb_resume_trace/0,
ttb_get_filenames/1]).
-define(CHUNKSIZE,8191). % 8 kbytes - 1 byte
@@ -658,22 +659,42 @@ stop_seq_trace() ->
%% Fetch ttb logs from remote node
ttb_fetch(MetaFile,{Port,Host}) ->
+ ttb_fetch(MetaFile,{Port,Host},undefined).
+ttb_fetch(MetaFile,{Port,Host},MasterEnc) ->
erlang:process_flag(priority,low),
Files = ttb_get_filenames(MetaFile),
{ok, Sock} = gen_tcp:connect(Host, Port, [binary, {packet, 2}]),
- send_files({Sock,Host},Files),
+ send_files({Sock,Host},Files,MasterEnc,file:native_name_encoding()),
ok = gen_tcp:close(Sock).
-send_files({Sock,Host},[File|Files]) ->
+send_files({Sock,Host},[File|Files],MasterEnc,MyEnc) ->
{ok,Fd} = file:open(File,[raw,read,binary]),
- ok = gen_tcp:send(Sock,<<1,(list_to_binary(filename:basename(File)))/binary>>),
+ Basename = filename:basename(File),
+ {Code,FilenameBin} = encode_filename(Basename,MasterEnc,MyEnc),
+ ok = gen_tcp:send(Sock,<<Code,FilenameBin/binary>>),
send_chunks(Sock,Fd),
ok = file:delete(File),
- send_files({Sock,Host},Files);
-send_files({_Sock,_Host},[]) ->
+ send_files({Sock,Host},Files,MasterEnc,MyEnc);
+send_files({_Sock,_Host},[],_MasterEnc,_MyEnc) ->
done.
+encode_filename(Basename,undefined,MyEnc) ->
+ %% Compatible with old version of ttb.erl, but no longer crashing
+ %% for code points > 255.
+ {1,unicode:characters_to_binary(Basename,MyEnc,MyEnc)};
+encode_filename(Basename,MasterEnc,MyEnc) ->
+ case unicode:characters_to_binary(Basename,MyEnc,MasterEnc) of
+ Bin when is_binary(Bin) ->
+ %% Encoding succeeded
+ {2,Bin};
+ _ ->
+ %% Can't convert Basename from my encoding to the master
+ %% node's encoding. Doing my best and hoping that master
+ %% node can fix it...
+ {3,unicode:characters_to_binary(Basename,MyEnc,MyEnc)}
+ end.
+
send_chunks(Sock,Fd) ->
case file:read(Fd,?CHUNKSIZE) of
{ok,Bin} ->
diff --git a/lib/sasl/doc/src/sasl_app.xml b/lib/sasl/doc/src/sasl_app.xml
index 0576397f9b..e0693fcb60 100644
--- a/lib/sasl/doc/src/sasl_app.xml
+++ b/lib/sasl/doc/src/sasl_app.xml
@@ -103,13 +103,16 @@
<tag><c>{file,FileName}</c></tag>
<item><p>Installs <c>sasl_report_file_h</c> in the error logger.
All reports go to file <c>FileName</c>, which is a
- string.</p></item>
+ string. The file is opened in <c>write</c> mode with encoding
+ <c>utf8</c>.</p></item>
<tag><c>{file,FileName,Modes}</c></tag>
<item><p>Same as <c>{file,FileName}</c>, except that <c>Modes</c>
allows you to specify the modes used for opening the <c>FileName</c>
given to the <seealso marker="kernel:file#open/2">file:open/2</seealso>
- call. When not specified, <c>Modes</c> defaults to <c>[write]</c>.
- Use <c>[append]</c> to have the <c>FileName</c> open in append mode.
+ call. By default, the file is opened in <c>write</c> mode
+ with encoding <c>utf8</c>. Use <c>[append]</c> to have
+ the <c>FileName</c> open in append mode. A different
+ encoding can also be specified.
<c>FileName</c> is a string.</p></item>
<tag><c>false</c></tag>
<item><p>No SASL error logger handler is installed.</p></item>
diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl
index eb454155d5..e6556ec6ce 100644
--- a/lib/sasl/src/sasl_report.erl
+++ b/lib/sasl/src/sasl_report.erl
@@ -47,6 +47,7 @@ io_report(_IO, _Fd, _, _) ->
is_my_error_report(all, Type) -> is_my_error_report(Type);
is_my_error_report(error, Type) -> is_my_error_report(Type);
is_my_error_report(_, _Type) -> false.
+
is_my_error_report(supervisor_report) -> true;
is_my_error_report(crash_report) -> true;
is_my_error_report(_) -> false.
@@ -54,6 +55,7 @@ is_my_error_report(_) -> false.
is_my_info_report(all, Type) -> is_my_info_report(Type);
is_my_info_report(progress, Type) -> is_my_info_report(Type);
is_my_info_report(_, _Type) -> false.
+
is_my_info_report(progress) -> true;
is_my_info_report(_) -> false.
@@ -62,46 +64,65 @@ write_report2(IO, Fd, Head, supervisor_report, Report) ->
Context = sup_get(errorContext, Report),
Reason = sup_get(reason, Report),
Offender = sup_get(offender, Report),
- {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender]),
- write_report_action(IO, Fd, Head, FmtString, Args);
+ Enc = encoding(Fd),
+ {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender], Enc),
+ String = io_lib:format(FmtString, Args),
+ write_report_action(IO, Fd, Head, String);
write_report2(IO, Fd, Head, progress, Report) ->
- Format = format_key_val(Report),
- write_report_action(IO, Fd, Head, "~s", [Format]);
+ Encoding = encoding(Fd),
+ Depth = error_logger:get_format_depth(),
+ String = format_key_val(Report, Encoding, Depth),
+ write_report_action(IO, Fd, Head, String);
write_report2(IO, Fd, Head, crash_report, Report) ->
+ Encoding = encoding(Fd),
Depth = error_logger:get_format_depth(),
- Format = proc_lib:format(Report, latin1, Depth),
- write_report_action(IO, Fd, Head, "~s", [Format]).
-
-supervisor_format(Args0) ->
- case error_logger:get_format_depth() of
- unlimited ->
- {" Supervisor: ~p~n"
- " Context: ~p~n"
- " Reason: ~80.18p~n"
- " Offender: ~80.18p~n~n",
- Args0};
- Depth ->
- [A,B,C,D] = Args0,
- Args = [A,Depth,B,Depth,C,Depth,D,Depth],
- {" Supervisor: ~P~n"
- " Context: ~P~n"
- " Reason: ~80.18P~n"
- " Offender: ~80.18P~n~n",
- Args}
- end.
-
-write_report_action(IO, Fd, Head, Format, Args) ->
- S = [Head|io_lib:format(Format, Args)],
+ String = proc_lib:format(Report, Encoding, Depth),
+ write_report_action(IO, Fd, Head, String).
+
+supervisor_format(Args0, Encoding) ->
+ {P, Tl} = p(Encoding, error_logger:get_format_depth()),
+ [A,B,C,D] = Args0,
+ Args = [A|Tl] ++ [B|Tl] ++ [C|Tl] ++ [D|Tl],
+ {" Supervisor: ~" ++ P ++ "\n"
+ " Context: ~" ++ P ++ "\n"
+ " Reason: ~80.18" ++ P ++ "\n"
+ " Offender: ~80.18" ++ P ++ "\n~n",
+ Args}.
+
+write_report_action(IO, Fd, Head, String) ->
+ S = [Head|String],
case IO of
io -> io:put_chars(Fd, S);
io_lib -> S
end.
-format_key_val([{Tag,Data}|Rep]) ->
- io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep);
-format_key_val(_) ->
+format_key_val(Rep, Encoding, Depth) ->
+ {P, Tl} = p(Encoding, Depth),
+ format_key_val1(Rep, P, Tl).
+
+format_key_val1([{Tag,Data}|Rep], P, Tl) ->
+ (io_lib:format(" ~16w: ~" ++ P ++ "\n", [Tag, Data|Tl]) ++
+ format_key_val1(Rep, P, Tl));
+format_key_val1(_, _, _) ->
[].
+p(Encoding, Depth) ->
+ {Letter, Tl} = case Depth of
+ unlimited -> {"p", []};
+ _ -> {"P", [Depth]}
+ end,
+ P = modifier(Encoding) ++ Letter,
+ {P, Tl}.
+
+encoding(IO) ->
+ case lists:keyfind(encoding, 1, io:getopts(IO)) of
+ false -> latin1;
+ {encoding, Enc} -> Enc
+ end.
+
+modifier(latin1) -> "";
+modifier(_) -> "t".
+
sup_get(Tag, Report) ->
case lists:keysearch(Tag, 1, Report) of
{value, {_, Value}} ->
diff --git a/lib/sasl/src/sasl_report_file_h.erl b/lib/sasl/src/sasl_report_file_h.erl
index 21746839fa..d3b5c7dc0d 100644
--- a/lib/sasl/src/sasl_report_file_h.erl
+++ b/lib/sasl/src/sasl_report_file_h.erl
@@ -29,15 +29,27 @@
handle_event/2, handle_call/2, handle_info/2,
terminate/2]).
-init({File, Modes, Type}) when is_list(Modes) ->
+init({File, Modes0, Type}) when is_list(Modes0) ->
process_flag(trap_exit, true),
+ Modes1 =
+ case lists:keymember(encoding,1,Modes0) of
+ true -> Modes0;
+ false -> [{encoding,utf8}|Modes0]
+ end,
+ Modes =
+ case [M || M <- Modes1, lists:member(M,[write,append,exclusive])] of
+ [] ->
+ [write|Modes1];
+ _ ->
+ Modes1
+ end,
case file:open(File, Modes) of
{ok,Fd} ->
{ok, {Fd, File, Type}};
What ->
What
end.
-
+
handle_event({_Type, GL, _Msg}, State) when node(GL) /= node() ->
{ok, State};
handle_event(Event, {Fd, File, Type}) ->
diff --git a/lib/sasl/test/sasl_report_SUITE.erl b/lib/sasl/test/sasl_report_SUITE.erl
index 53fb614921..92df5e6e40 100644
--- a/lib/sasl/test/sasl_report_SUITE.erl
+++ b/lib/sasl/test/sasl_report_SUITE.erl
@@ -20,7 +20,7 @@
-module(sasl_report_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([gen_server_crash/1]).
+-export([gen_server_crash/1, gen_server_crash_unicode/1]).
-export([crash_me/0,start_link/0,init/1,handle_cast/2,terminate/2]).
@@ -29,7 +29,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [gen_server_crash].
+ [gen_server_crash, gen_server_crash_unicode].
groups() ->
[].
@@ -47,8 +47,14 @@ end_per_group(_GroupName, Config) ->
Config.
gen_server_crash(Config) ->
+ gen_server_crash(Config, latin1).
+
+gen_server_crash_unicode(Config) ->
+ gen_server_crash(Config, unicode).
+
+gen_server_crash(Config, Encoding) ->
try
- do_gen_server_crash(Config)
+ do_gen_server_crash(Config, Encoding)
after
error_logger:tty(true),
ok = application:unset_env(sasl, sasl_error_logger),
@@ -57,7 +63,7 @@ gen_server_crash(Config) ->
end,
ok.
-do_gen_server_crash(Config) ->
+do_gen_server_crash(Config, Encoding) ->
PrivDir = ?config(priv_dir, Config),
LogDir = filename:join(PrivDir, ?MODULE),
KernelLog = filename:join(LogDir, "kernel.log"),
@@ -67,7 +73,8 @@ do_gen_server_crash(Config) ->
error_logger:delete_report_handler(cth_log_redirect),
error_logger:tty(false),
application:stop(sasl),
- ok = application:set_env(sasl, sasl_error_logger, {file,SaslLog},
+ Modes = [write, {encoding, Encoding}],
+ ok = application:set_env(sasl, sasl_error_logger, {file,SaslLog,Modes},
[{persistent,true}]),
application:set_env(kernel, error_logger_format_depth, 30),
error_logger:logfile({open,KernelLog}),
@@ -78,16 +85,21 @@ do_gen_server_crash(Config) ->
error_logger:logfile(close),
- check_file(KernelLog, 70000, 150000),
- check_file(SaslLog, 100000, 150000),
+ check_file(KernelLog, utf8, 70000, 150000),
+ check_file(SaslLog, Encoding, 70000, 150000),
+ %% ok = file:delete(KernelLog),
+ %% ok = file:delete(SaslLog),
ok.
-check_file(File, Min, Max) ->
+check_file(File, Encoding, Min, Max) ->
{ok,Bin} = file:read_file(File),
Base = filename:basename(File),
io:format("*** Contents of ~s ***\n", [Base]),
- io:put_chars([Bin,"\n"]),
+ case Encoding of
+ latin1 -> io:format("~s\n", [Bin]);
+ _ -> io:format("~ts\n", [Bin])
+ end,
Sz = byte_size(Bin),
io:format("Size: ~p (allowed range is ~p..~p)\n",
[Sz,Min,Max]),
@@ -110,7 +122,9 @@ crash_me() ->
{ok,SuperPid} = supervisor:start_link(sasl_report_suite_supervisor, []),
[{Id,Pid,_,_}] = supervisor:which_children(SuperPid),
HugeData = gb_sets:from_list(lists:seq(1, 100000)),
- gen_server:cast(Pid, HugeData),
+ SomeData1 = list_to_atom([246]),
+ SomeData2 = list_to_atom([1024]),
+ gen_server:cast(Pid, {HugeData,SomeData1,SomeData2}),
Ref = monitor(process, Pid),
receive
{'DOWN',Ref,process,Pid,_} ->
@@ -129,6 +143,12 @@ init(_) ->
handle_cast(Big, St) ->
Seq = lists:seq(1, 10000),
+ Latin1Atom = list_to_atom([246]),
+ UnicodeAtom = list_to_atom([1024]),
+ put(Latin1Atom, Latin1Atom),
+ put(UnicodeAtom, UnicodeAtom),
+ self() ! Latin1Atom,
+ self() ! UnicodeAtom,
self() ! Seq,
self() ! Seq,
self() ! Seq,
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index 2749feb1eb..db415a3666 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -32,7 +32,7 @@
-include("ssl_record.hrl").
-include("ssl_internal.hrl").
--export([decode/1, alert_txt/1, reason_code/2]).
+-export([decode/1, own_alert_txt/1, alert_txt/1, reason_code/2]).
%%====================================================================
%% Internal application API
@@ -60,12 +60,28 @@ reason_code(#alert{description = Description}, _) ->
{tls_alert, string:to_lower(description_txt(Description))}.
%%--------------------------------------------------------------------
+-spec own_alert_txt(#alert{}) -> string().
+%%
+%% Description: Returns the error string for given alert generated
+%% by the erlang implementation.
+%%--------------------------------------------------------------------
+own_alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}, reason = undefined, role = Role}) ->
+ "at " ++ Mod ++ ":" ++ integer_to_list(Line) ++ " generated " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
+ level_txt(Level) ++ description_txt(Description);
+own_alert_txt(#alert{reason = Reason} = Alert) ->
+ BaseTxt = own_alert_txt(Alert#alert{reason = undefined}),
+ FormatDepth = 9, % Some limit on printed representation of an error
+ ReasonTxt = lists:flatten(io_lib:format("~P", [Reason, FormatDepth])),
+ BaseTxt ++ " - " ++ ReasonTxt.
+
+%%--------------------------------------------------------------------
-spec alert_txt(#alert{}) -> string().
%%
-%% Description: Returns the error string for given alert.
+%% Description: Returns the error string for given alert received from
+%% the peer.
%%--------------------------------------------------------------------
-alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}, reason = undefined, role = Role}) ->
- "at " ++ Mod ++ ":" ++ integer_to_list(Line) ++ " " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
+alert_txt(#alert{level = Level, description = Description, reason = undefined, role = Role}) ->
+ "received " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
level_txt(Level) ++ description_txt(Description);
alert_txt(#alert{reason = Reason} = Alert) ->
BaseTxt = alert_txt(Alert#alert{reason = undefined}),
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 5cd66387ae..b031d3d47b 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1144,7 +1144,8 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName,
port = Port, session = Session, user_application = {_Mon, Pid},
role = Role, socket_options = Opts, tracker = Tracker}) ->
invalidate_session(Role, Host, Port, Session),
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(),
+ StateName, Alert#alert{role = opposite_role(Role)}),
alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection),
{stop, normal};
@@ -1155,7 +1156,8 @@ handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
#state{role = Role, ssl_options = SslOpts, protocol_cb = Connection, renegotiation = {true, internal}} = State) ->
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
handle_normal_shutdown(Alert, StateName, State),
{stop, {shutdown, peer_close}};
@@ -1163,7 +1165,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
#state{role = Role,
ssl_options = SslOpts, renegotiation = {true, From},
protocol_cb = Connection} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
gen_statem:reply(From, {error, renegotiation_rejected}),
{Record, State} = Connection:next_record(State0),
%% Go back to connection!
@@ -1172,7 +1175,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
%% Gracefully log and ignore all other warning alerts
handle_alert(#alert{level = ?WARNING} = Alert, StateName,
#state{ssl_options = SslOpts, protocol_cb = Connection, role = Role} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
{Record, State} = Connection:next_record(State0),
Connection:next_event(StateName, Record, State).
@@ -2372,10 +2376,13 @@ alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Connectio
Transport, Socket, Connection, Tracker), ReasonCode})
end.
-log_alert(true, ProtocolName, StateName, Alert) ->
+log_alert(true, Role, ProtocolName, StateName, #alert{role = Role} = Alert) ->
+ Txt = ssl_alert:own_alert_txt(Alert),
+ error_logger:info_report(io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]));
+log_alert(true, Role, ProtocolName, StateName, Alert) ->
Txt = ssl_alert:alert_txt(Alert),
- error_logger:format("~s: In state ~p ~s\n", [ProtocolName, StateName, Txt]);
-log_alert(false, _, _, _) ->
+ error_logger:info_report(io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]));
+log_alert(false, _, _, _, _) ->
ok.
handle_own_alert(Alert, Version, StateName,
@@ -2393,7 +2400,7 @@ handle_own_alert(Alert, Version, StateName,
ignore
end,
try %% Try to tell the local user
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = Role}),
+ log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = Role}),
handle_normal_shutdown(Alert,StateName, State)
catch _:_ ->
ok
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index ca9aaf4660..ef7c3de7af 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -563,7 +563,7 @@ server_register_session(Port, Session, #state{session_cache_server_max = Max,
do_register_session(Key, Session, Max, Pid, Cache, CacheCb) ->
try CacheCb:size(Cache) of
- Max ->
+ Size when Size >= Max ->
invalidate_session_cache(Pid, CacheCb, Cache);
_ ->
CacheCb:update(Cache, Key, Session),
diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl
index c48ccfb83b..64e8042b25 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -200,8 +200,14 @@ common_init_per_group(GroupName, Config) ->
openssl_check(GroupName, Config)
end.
-end_per_group(_GroupName, Config) ->
- proplists:delete(tls_version, Config).
+end_per_group(GroupName, Config0) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ Config = ssl_test_lib:clean_tls_version(Config0),
+ proplists:delete(tls_version, Config);
+ false ->
+ Config0
+ end.
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
index bd9011f3b6..055f05a900 100644
--- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
@@ -103,8 +103,14 @@ init_per_group(GroupName, Config) ->
Config
end.
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
init_per_testcase(_TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index e4faf267b7..9efde4752f 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -276,13 +276,12 @@ end_per_suite(_Config) ->
application:stop(crypto).
%%--------------------------------------------------------------------
-init_per_group(GroupName, Config) when GroupName == basic;
- GroupName == basic_tls;
- GroupName == options;
- GroupName == options_tls;
- GroupName == session ->
- ssl_test_lib:init_tls_version_default(Config);
+init_per_group(GroupName, Config) when GroupName == basic_tls;
+ GroupName == options_tls;
+ GroupName == basic;
+ GroupName == options ->
+ ssl_test_lib:clean_tls_version(Config);
init_per_group(GroupName, Config) ->
case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of
true ->
@@ -297,8 +296,13 @@ init_per_group(GroupName, Config) ->
end
end.
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
%%--------------------------------------------------------------------
init_per_testcase(Case, Config) when Case == unordered_protocol_versions_client;
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 6221cffdc1..c3fd73bf09 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -110,8 +110,8 @@ init_per_group(tls, Config0) ->
application:load(ssl),
application:set_env(ssl, protocol_version, Version),
ssl:start(),
- Config = proplists:delete(protocol, Config0),
- [{protocol, tls}, {version, tls_record:protocol_version(Version)} | Config];
+ Config = ssl_test_lib:init_tls_version(Version, Config0),
+ [{version, tls_record:protocol_version(Version)} | Config];
init_per_group(dtls, Config0) ->
Version = dtls_record:protocol_version(dtls_record:highest_protocol_version([])),
@@ -119,8 +119,8 @@ init_per_group(dtls, Config0) ->
application:load(ssl),
application:set_env(ssl, protocol_version, Version),
ssl:start(),
- Config = proplists:delete(protocol_opts, proplists:delete(protocol, Config0)),
- [{protocol, dtls}, {protocol_opts, [{protocol, dtls}]}, {version, dtls_record:protocol_version(Version)} | Config];
+ Config = ssl_test_lib:init_tls_version(Version, Config0),
+ [{version, dtls_record:protocol_version(Version)} | Config];
init_per_group(active, Config) ->
[{active, true}, {receive_function, send_recv_result_active} | Config];
@@ -134,6 +134,9 @@ init_per_group(error_handling, Config) ->
init_per_group(_, Config) ->
Config.
+end_per_group(GroupName, Config) when GroupName == tls;
+ GroupName == dtls ->
+ ssl_test_lib:clean_tls_version(Config);
end_per_group(_GroupName, Config) ->
Config.
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
index a02881f1ae..6bf2aa2786 100644
--- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -95,8 +95,13 @@ init_per_group(GroupName, Config) ->
Config
end.
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
init_per_testcase(_TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index 5e40200158..408d62ce9c 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -185,8 +185,13 @@ init_per_group(GroupName, Config) ->
end.
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
init_per_testcase(_TestCase, Config) ->
ct:timetrap({seconds, ?BASE_TIMEOUT_SECONDS}),
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index cb1957327a..ef05241759 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -95,8 +95,13 @@ init_per_group(GroupName, Config) ->
Config
end.
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_huge;
TestCase == server_echos_active_once_huge;
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index c919f901a1..ba061cb19c 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1115,8 +1115,7 @@ init_tls_version(Version, Config) ->
NewConfig = proplists:delete(protocol_opts, proplists:delete(protocol, Config)),
[{protocol, tls} | NewConfig].
-init_tls_version_default(Config) ->
- %% Remove non default options that may be left from other test groups
+clean_tls_version(Config) ->
proplists:delete(protocol_opts, proplists:delete(protocol, Config)).
sufficient_crypto_support(Version)
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 43a4a0b6d1..c4fe97d88e 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -182,8 +182,13 @@ init_per_group(GroupName, Config) ->
Config
end.
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
init_per_testcase(expired_session, Config) ->
ct:timetrap(?EXPIRE * 1000 * 5),
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 10e8c9c800..4e3fe0e5c1 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1354,7 +1354,7 @@ open_file_loop2(Head, N) ->
?MODULE, [], Head);
Message ->
error_logger:format("** dets: unexpected message"
- "(ignored): ~w~n", [Message]),
+ "(ignored): ~tw~n", [Message]),
open_file_loop(Head, N)
end.
@@ -1403,7 +1403,7 @@ apply_op(Op, From, Head, N) ->
Head;
_Dirty when N =:= 0 -> % dirty or new_dirty
%% The updates seems to have declined
- dets_utils:vformat("** dets: Auto save of ~p\n",
+ dets_utils:vformat("** dets: Auto save of ~tp\n",
[Head#head.name]),
{NewHead, _Res} = perform_save(Head, true),
erlang:garbage_collect(),
@@ -1587,13 +1587,13 @@ bug_found(Name, Op, Bad, From) ->
%% If stream_op/5 found more requests, this is not
%% the last operation.
error_logger:format
- ("** dets: Bug was found when accessing table ~w,~n"
- "** dets: operation was ~p and reply was ~w.~n"
- "** dets: Stacktrace: ~w~n",
+ ("** dets: Bug was found when accessing table ~tw,~n"
+ "** dets: operation was ~tp and reply was ~tw.~n"
+ "** dets: Stacktrace: ~tw~n",
[Name, Op, Bad, erlang:get_stacktrace()]);
false ->
error_logger:format
- ("** dets: Bug was found when accessing table ~w~n",
+ ("** dets: Bug was found when accessing table ~tw~n",
[Name])
end,
if
@@ -2117,7 +2117,7 @@ do_open_file([Fname, Verbose], Parent, Server, Ref) ->
Error;
Bad ->
error_logger:format
- ("** dets: Bug was found in open_file/1, reply was ~w.~n",
+ ("** dets: Bug was found in open_file/1, reply was ~tw.~n",
[Bad]),
{error, {dets_bug, Fname, Bad}}
end;
@@ -2135,7 +2135,7 @@ do_open_file([Tab, OpenArgs, Verb], Parent, Server, _Ref) ->
Bad ->
error_logger:format
("** dets: Bug was found in open_file/2, arguments were~n"
- "** dets: ~w and reply was ~w.~n",
+ "** dets: ~tw and reply was ~tw.~n",
[OpenArgs, Bad]),
{error, {dets_bug, Tab, {open_file, OpenArgs}, Bad}}
end.
@@ -3123,7 +3123,7 @@ check_safe_fixtable(Head) ->
((get(verbose) =:= yes) orelse dets_utils:debug_mode()) of
true ->
error_logger:format
- ("** dets: traversal of ~p needs safe_fixtable~n",
+ ("** dets: traversal of ~tp needs safe_fixtable~n",
[Head#head.name]);
false ->
ok
@@ -3189,7 +3189,7 @@ scan_read(H, From, _To, Min, _L, Ts, R, C) ->
err(Error) ->
case get(verbose) of
yes ->
- error_logger:format("** dets: failed with ~w~n", [Error]),
+ error_logger:format("** dets: failed with ~tw~n", [Error]),
Error;
undefined ->
Error
diff --git a/lib/stdlib/src/dets_utils.erl b/lib/stdlib/src/dets_utils.erl
index da6ebd18f2..17f55ebdc2 100644
--- a/lib/stdlib/src/dets_utils.erl
+++ b/lib/stdlib/src/dets_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -387,7 +387,7 @@ corrupt_reason(Head, Reason0) ->
corrupt(Head, Error) ->
case get(verbose) of
yes ->
- error_logger:format("** dets: Corrupt table ~p: ~tp\n",
+ error_logger:format("** dets: Corrupt table ~tp: ~tp\n",
[Head#head.name, Error]);
_ -> ok
end,
diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl
index a1a97af4c5..bdcefda6e5 100644
--- a/lib/stdlib/src/edlin_expand.erl
+++ b/lib/stdlib/src/edlin_expand.erl
@@ -23,7 +23,7 @@
-export([expand/1, format_matches/1]).
--import(lists, [reverse/1, nthtail/2, prefix/2]).
+-import(lists, [reverse/1, prefix/2]).
%% expand(CurrentBefore) ->
%% {yes, Expansion, Matches} | {no, Matches}
@@ -75,15 +75,15 @@ to_atom(Str) ->
end.
match(Prefix, Alts, Extra0) ->
- Len = length(Prefix),
+ Len = string:length(Prefix),
Matches = lists:sort(
[{S, A} || {H, A} <- Alts,
- prefix(Prefix, S=hd(io_lib:fwrite("~w",[H])))]),
+ prefix(Prefix, S=flat_write(H))]),
case longest_common_head([N || {N, _} <- Matches]) of
{partial, []} ->
{no, [], Matches}; % format_matches(Matches)};
{partial, Str} ->
- case nthtail(Len, Str) of
+ case string:slice(Str, Len) of
[] ->
{yes, [], Matches}; % format_matches(Matches)};
Remain ->
@@ -94,18 +94,21 @@ match(Prefix, Alts, Extra0) ->
{"(",[{Str,0}]} -> "()";
{_,_} -> Extra0
end,
- {yes, nthtail(Len, Str) ++ Extra, []};
+ {yes, string:slice(Str, Len) ++ Extra, []};
no ->
{no, [], []}
end.
+flat_write(T) ->
+ lists:flatten(io_lib:fwrite("~tw",[T])).
+
%% Return the list of names L in multiple columns.
format_matches(L) ->
{S1, Dots} = format_col(lists:sort(L), []),
S = case Dots of
true ->
{_, Prefix} = longest_common_head(vals(L)),
- PrefixLen = length(Prefix),
+ PrefixLen = string:length(Prefix),
case PrefixLen =< 3 of
true -> S1; % Do not replace the prefix with "...".
false ->
@@ -128,7 +131,7 @@ format_col([A|T], Width, Len, Acc0, LL, Dots) ->
{H0, R} = format_val(A),
Hmax = LL - length(R),
{H, NewDots} =
- case length(H0) > Hmax of
+ case string:length(H0) > Hmax of
true -> {io_lib:format("~-*ts", [Hmax - 3, H0]) ++ "...", true};
false -> {H0, Dots}
end,
@@ -149,12 +152,12 @@ format_val(H) ->
field_width(L, LL) -> field_width(L, 0, LL).
field_width([{H,_}|T], W, LL) ->
- case length(H) of
+ case string:length(H) of
L when L > W -> field_width(T, L, LL);
_ -> field_width(T, W, LL)
end;
field_width([H|T], W, LL) ->
- case length(H) of
+ case string:length(H) of
L when L > W -> field_width(T, L, LL);
_ -> field_width(T, W, LL)
end;
@@ -169,10 +172,11 @@ vals([S|L]) -> [S|vals(L)].
leading_dots([], _Len) -> [];
leading_dots([{H, I}|L], Len) ->
- [{"..." ++ nthtail(Len, H), I}|leading_dots(L, Len)];
+ [{"..." ++ string:slice(H, Len), I}|leading_dots(L, Len)];
leading_dots([H|L], Len) ->
- ["..." ++ nthtail(Len, H)|leading_dots(L, Len)].
+ ["..." ++ string:slice(H, Len)|leading_dots(L, Len)].
+%% Strings are handled naively, but it should be OK here.
longest_common_head([]) ->
no;
longest_common_head(LL) ->
diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl
index b7c193f965..58da0cbdd6 100644
--- a/lib/stdlib/src/error_logger_file_h.erl
+++ b/lib/stdlib/src/error_logger_file_h.erl
@@ -126,7 +126,7 @@ format_body(State, [{Format,Args}|T]) ->
S0
catch
_:_ ->
- format(State, "ERROR: ~p - ~p\n", [Format,Args])
+ format(State, "ERROR: ~tp - ~tp\n", [Format,Args])
end,
[S|format_body(State, T)];
format_body(_State, []) ->
@@ -165,44 +165,26 @@ parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
parse_event(_) -> ignore.
format_term(Term) when is_list(Term) ->
- case string_p(Term) of
+ case string_p(lists:flatten(Term)) of
true ->
- [{"~s\n",[Term]}];
+ [{"~ts\n",[Term]}];
false ->
format_term_list(Term)
end;
format_term(Term) ->
- [{"~p\n",[Term]}].
+ [{"~tp\n",[Term]}].
format_term_list([{Tag,Data}|T]) ->
- [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
+ [{" ~tp: ~tp\n",[Tag,Data]}|format_term_list(T)];
format_term_list([Data|T]) ->
- [{" ~p\n",[Data]}|format_term_list(T)];
+ [{" ~tp\n",[Data]}|format_term_list(T)];
format_term_list([]) ->
- [];
-format_term_list(_) ->
- %% Continue to allow non-proper lists for now.
- %% FIXME: Remove this clause in OTP 19.
[].
string_p([]) ->
false;
-string_p(Term) ->
- string_p1(Term).
-
-string_p1([H|T]) when is_integer(H), H >= $\s, H < 255 ->
- string_p1(T);
-string_p1([$\n|T]) -> string_p1(T);
-string_p1([$\r|T]) -> string_p1(T);
-string_p1([$\t|T]) -> string_p1(T);
-string_p1([$\v|T]) -> string_p1(T);
-string_p1([$\b|T]) -> string_p1(T);
-string_p1([$\f|T]) -> string_p1(T);
-string_p1([$\e|T]) -> string_p1(T);
-string_p1([H|T]) when is_list(H) ->
- string_p1(H) andalso string_p1(T);
-string_p1([]) -> true;
-string_p1(_) -> false.
+string_p(FlatList) ->
+ io_lib:printable_list(FlatList).
get_utc_config() ->
%% SASL utc_log configuration overrides stdlib config
@@ -225,7 +207,7 @@ header(Time, Title) ->
end.
header({{Y,Mo,D},{H,Mi,S}}, Title, UTC) ->
- io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s ~s===~n",
+ io_lib:format("~n=~ts==== ~p-~s-~p::~s:~s:~s ~s===~n",
[Title,D,month(Mo),Y,t(H),t(Mi),t(S),UTC]).
t(X) when is_integer(X) ->
diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl
index 8f0d7b0362..fa940b7264 100644
--- a/lib/stdlib/src/error_logger_tty_h.erl
+++ b/lib/stdlib/src/error_logger_tty_h.erl
@@ -39,13 +39,16 @@
{user,
prev_handler,
io_mod=io,
- depth=unlimited}).
+ depth=unlimited,
+ modifier=""}).
%% This one is used when we takeover from the simple error_logger.
init({[], {error_logger, Buf}}) ->
User = set_group_leader(),
Depth = error_logger:get_format_depth(),
- State = #st{user=User,prev_handler=error_logger,depth=Depth},
+ Modifier = modifier(),
+ State = #st{user=User,prev_handler=error_logger,
+ depth=Depth,modifier=Modifier},
write_events(State, Buf),
{ok, State};
%% This one is used if someone took over from us, and now wants to
@@ -57,7 +60,8 @@ init({[], {error_logger_tty_h, PrevHandler}}) ->
init([]) ->
User = set_group_leader(),
Depth = error_logger:get_format_depth(),
- {ok, #st{user=User,prev_handler=[],depth=Depth}}.
+ Modifier = modifier(),
+ {ok, #st{user=User,prev_handler=[],depth=Depth,modifier=Modifier}}.
handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
{ok, State};
@@ -91,8 +95,9 @@ code_change(_OldVsn, State, _Extra) ->
write_event(Event, IoMod) ->
do_write_event(#st{io_mod=IoMod}, Event).
-write_event(Event, IoMod, Depth) ->
- do_write_event(#st{io_mod=IoMod,depth=Depth}, Event).
+write_event(Event, IoMod, {Depth, Enc}) ->
+ Modifier = modifier(Enc),
+ do_write_event(#st{io_mod=IoMod,depth=Depth,modifier=Modifier}, Event).
%%% ------------------------------------------------------
@@ -120,12 +125,12 @@ write_events(State, [Ev|Es]) ->
write_events(_State, []) ->
ok.
-do_write_event(State, {Time, Event}) ->
- case parse_event(Event) of
+do_write_event(#st{modifier=M}=State, {Time, Event}) ->
+ case parse_event(Event,M) of
ignore ->
ok;
{Title,Pid,FormatList} ->
- Header = header(Time, Title),
+ Header = header(Time, Title, M),
Body = format_body(State, FormatList),
AtNode = if
node(Pid) =/= node() ->
@@ -144,13 +149,13 @@ do_write_event(State, {Time, Event}) ->
do_write_event(_, _) ->
ok.
-format_body(State, [{Format,Args}|T]) ->
+format_body(#st{modifier=M}=State, [{Format,Args}|T]) ->
S = try format(State, Format, Args) of
S0 ->
S0
catch
_:_ ->
- format(State, "ERROR: ~p - ~p\n", [Format,Args])
+ format(State, "ERROR: ~"++M++"p - ~"++M++"p\n", [Format,Args])
end,
[S|format_body(State, T)];
format_body(_State, []) ->
@@ -174,62 +179,41 @@ limit_format([H|T], Depth) ->
limit_format([], _) ->
[].
-parse_event({error, _GL, {Pid, Format, Args}}) ->
+parse_event({error, _GL, {Pid, Format, Args}},_) ->
{"ERROR REPORT",Pid,[{Format,Args}]};
-parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
+parse_event({info_msg, _GL, {Pid, Format, Args}},_) ->
{"INFO REPORT",Pid,[{Format, Args}]};
-parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
+parse_event({warning_msg, _GL, {Pid, Format, Args}},_) ->
{"WARNING REPORT",Pid,[{Format,Args}]};
-parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
- {"ERROR REPORT",Pid,format_term(Args)};
-parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
- {"INFO REPORT",Pid,format_term(Args)};
-parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
- {"WARNING REPORT",Pid,format_term(Args)};
-parse_event(_) -> ignore.
-
-format_term(Term) when is_list(Term) ->
- case string_p(Term) of
+parse_event({error_report, _GL, {Pid, std_error, Args}},M) ->
+ {"ERROR REPORT",Pid,format_term(Args,M)};
+parse_event({info_report, _GL, {Pid, std_info, Args}},M) ->
+ {"INFO REPORT",Pid,format_term(Args,M)};
+parse_event({warning_report, _GL, {Pid, std_warning, Args}},M) ->
+ {"WARNING REPORT",Pid,format_term(Args,M)};
+parse_event(_,_) -> ignore.
+
+format_term(Term,M) when is_list(Term) ->
+ case string_p(lists:flatten(Term)) of
true ->
- [{"~s\n",[Term]}];
+ [{"~"++M++"s\n",[Term]}];
false ->
- format_term_list(Term)
+ format_term_list(Term,M)
end;
-format_term(Term) ->
- [{"~p\n",[Term]}].
-
-format_term_list([{Tag,Data}|T]) ->
- [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
-format_term_list([Data|T]) ->
- [{" ~p\n",[Data]}|format_term_list(T)];
-format_term_list([]) ->
- [];
-format_term_list(_) ->
- %% Continue to allow non-proper lists for now.
- %% FIXME: Remove this clause in OTP 19.
+format_term(Term,M) ->
+ [{"~"++M++"p\n",[Term]}].
+
+format_term_list([{Tag,Data}|T],M) ->
+ [{" ~"++M++"p: ~"++M++"p\n",[Tag,Data]}|format_term_list(T,M)];
+format_term_list([Data|T],M) ->
+ [{" ~"++M++"p\n",[Data]}|format_term_list(T,M)];
+format_term_list([],_) ->
[].
string_p([]) ->
false;
-string_p(Term) ->
- string_p1(Term).
-
-string_p1([H|T]) when is_integer(H), H >= $\s, H < 255 ->
- string_p1(T);
-string_p1([$\n|T]) -> string_p1(T);
-string_p1([$\r|T]) -> string_p1(T);
-string_p1([$\t|T]) -> string_p1(T);
-string_p1([$\v|T]) -> string_p1(T);
-string_p1([$\b|T]) -> string_p1(T);
-string_p1([$\f|T]) -> string_p1(T);
-string_p1([$\e|T]) -> string_p1(T);
-string_p1([H|T]) when is_list(H) ->
- case string_p1(H) of
- true -> string_p1(T);
- _ -> false
- end;
-string_p1([]) -> true;
-string_p1(_) -> false.
+string_p(FlatList) ->
+ io_lib:printable_list(FlatList).
get_utc_config() ->
%% SASL utc_log configuration overrides stdlib config
@@ -243,16 +227,16 @@ get_utc_config() ->
end
end.
-header(Time, Title) ->
+header(Time, Title, M) ->
case get_utc_config() of
true ->
- header(Time, Title, "UTC ");
+ header(Time, Title, "UTC ", M);
_ ->
- header(calendar:universal_time_to_local_time(Time), Title, "")
+ header(calendar:universal_time_to_local_time(Time), Title, "", M)
end.
-header({{Y,Mo,D},{H,Mi,S}}, Title, UTC) ->
- io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s ~s===~n",
+header({{Y,Mo,D},{H,Mi,S}}, Title, UTC, M) ->
+ io_lib:format("~n=~"++M++"s==== ~p-~s-~p::~s:~s:~s ~s===~n",
[Title,D,month(Mo),Y,t(H),t(Mi),t(S),UTC]).
t(X) when is_integer(X) ->
@@ -274,3 +258,13 @@ month(9) -> "Sep";
month(10) -> "Oct";
month(11) -> "Nov";
month(12) -> "Dec".
+
+modifier() ->
+ modifier(encoding()).
+modifier(latin1) ->
+ "";
+modifier(_) ->
+ "t".
+
+encoding() ->
+ proplists:get_value(encoding,io:getopts(),latin1).
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 2093916a7c..2b9d8ff65b 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -860,7 +860,7 @@ code_handler(Name, Args, Dict, File) ->
%% io:format("Calling:~p~n",[{Mod,Name,Args}]),
apply(Mod, Name, Args);
error ->
- io:format("Script does not export ~w/~w\n", [Name,Arity]),
+ io:format("Script does not export ~tw/~w\n", [Name,Arity]),
my_halt(127)
end
end.
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 898b2f5bba..b5d3cd3c8d 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -1693,7 +1693,7 @@ choice(Height, Width, P, Mode, Tab, Key, Turn, Opos) ->
end,
choice(Height, Width, P, Mode, Tab, Key, Turn, Opos);
[$/|Regexp] -> %% from regexp
- case re:compile(nonl(Regexp)) of
+ case re:compile(nonl(Regexp),[unicode]) of
{ok,Re} ->
re_search(Height, Width, Tab, ets:first(Tab), Re, 1, 1);
{error,{ErrorString,_Pos}} ->
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 32f43fc706..33af0aed8f 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -422,7 +422,7 @@ debug_options(Name, Opts) ->
try sys:debug_options(Options)
catch _:_ ->
error_logger:format(
- "~p: ignoring erroneous debug options - ~p~n",
+ "~tp: ignoring erroneous debug options - ~tp~n",
[Name,Options]),
[]
end;
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index da2b0da3ca..a9b98911e2 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -441,15 +441,15 @@ system_replace_state(StateFun, [ServerName, MSL, HibernateAfterTimeout, Hib]) ->
print_event(Dev, {in, Msg}, Name) ->
case Msg of
{notify, Event} ->
- io:format(Dev, "*DBG* ~p got event ~p~n", [Name, Event]);
+ io:format(Dev, "*DBG* ~tp got event ~tp~n", [Name, Event]);
{_,_,{call, Handler, Query}} ->
- io:format(Dev, "*DBG* ~p(~p) got call ~p~n",
+ io:format(Dev, "*DBG* ~tp(~tp) got call ~tp~n",
[Name, Handler, Query]);
_ ->
- io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
+ io:format(Dev, "*DBG* ~tp got ~tp~n", [Name, Msg])
end;
print_event(Dev, Dbg, Name) ->
- io:format(Dev, "*DBG* ~p : ~p~n", [Name, Dbg]).
+ io:format(Dev, "*DBG* ~tp : ~tp~n", [Name, Dbg]).
%% server_add_handler(Handler, Args, MSL) -> {Ret, MSL'}.
@@ -582,8 +582,8 @@ server_update(Handler1, Func, Event, SName) ->
remove, SName, normal),
no;
{'EXIT', {undef, [{Mod1, handle_info, [_,_], _}|_]}} ->
- error_logger:warning_msg("** Undefined handle_info in ~p~n"
- "** Unhandled message: ~p~n", [Mod1, Event]),
+ error_logger:warning_msg("** Undefined handle_info in ~tp~n"
+ "** Unhandled message: ~tp~n", [Mod1, Event]),
{ok, Handler1};
Other ->
do_terminate(Mod1, Handler1, {error, Other}, State,
@@ -767,10 +767,10 @@ report_error(Handler, Reason, State, LastIn, SName) ->
State
end,
error_msg("** gen_event handler ~p crashed.~n"
- "** Was installed in ~p~n"
- "** Last event was: ~p~n"
- "** When handler state == ~p~n"
- "** Reason == ~p~n",
+ "** Was installed in ~tp~n"
+ "** Last event was: ~tp~n"
+ "** When handler state == ~tp~n"
+ "** Reason == ~tp~n",
[handler(Handler),SName,LastIn,FmtState,Reason1]).
handler(Handler) when not Handler#handler.id ->
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 9ef0ca818c..96a53426e2 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -452,30 +452,30 @@ system_replace_state(StateFun, [Name, StateName, StateData, Mod, Time, Hibernate
print_event(Dev, {in, Msg}, {Name, StateName}) ->
case Msg of
{'$gen_event', Event} ->
- io:format(Dev, "*DBG* ~p got event ~p in state ~w~n",
+ io:format(Dev, "*DBG* ~tp got event ~tp in state ~tw~n",
[Name, Event, StateName]);
{'$gen_all_state_event', Event} ->
io:format(Dev,
- "*DBG* ~p got all_state_event ~p in state ~w~n",
+ "*DBG* ~tp got all_state_event ~tp in state ~tw~n",
[Name, Event, StateName]);
{timeout, Ref, {'$gen_timer', Message}} ->
io:format(Dev,
- "*DBG* ~p got timer ~p in state ~w~n",
+ "*DBG* ~tp got timer ~tp in state ~tw~n",
[Name, {timeout, Ref, Message}, StateName]);
{timeout, _Ref, {'$gen_event', Event}} ->
io:format(Dev,
- "*DBG* ~p got timer ~p in state ~w~n",
+ "*DBG* ~tp got timer ~tp in state ~tw~n",
[Name, Event, StateName]);
_ ->
- io:format(Dev, "*DBG* ~p got ~p in state ~w~n",
+ io:format(Dev, "*DBG* ~tp got ~tp in state ~tw~n",
[Name, Msg, StateName])
end;
print_event(Dev, {out, Msg, To, StateName}, Name) ->
- io:format(Dev, "*DBG* ~p sent ~p to ~w~n"
- " and switched to state ~w~n",
+ io:format(Dev, "*DBG* ~tp sent ~tp to ~tw~n"
+ " and switched to state ~tw~n",
[Name, Msg, To, StateName]);
print_event(Dev, return, {Name, StateName}) ->
- io:format(Dev, "*DBG* ~p switched to state ~w~n",
+ io:format(Dev, "*DBG* ~tp switched to state ~tw~n",
[Name, StateName]).
handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTimeout) -> %No debug here
@@ -500,7 +500,7 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTi
exit(R);
{'EXIT', {undef, [{Mod, handle_info, [_,_,_], _}|_]}} ->
error_logger:warning_msg("** Undefined handle_info in ~p~n"
- "** Unhandled message: ~p~n", [Mod, Msg]),
+ "** Unhandled message: ~tp~n", [Mod, Msg]),
loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, []);
{'EXIT', What} ->
terminate(What, Name, Msg, Mod, StateName, StateData, []);
@@ -620,29 +620,29 @@ error_info(Reason, Name, Msg, StateName, StateData, Debug) ->
_ ->
Reason
end,
- Str = "** State machine ~p terminating \n" ++
+ Str = "** State machine ~tp terminating \n" ++
get_msg_str(Msg) ++
- "** When State == ~p~n"
- "** Data == ~p~n"
- "** Reason for termination = ~n** ~p~n",
+ "** When State == ~tp~n"
+ "** Data == ~tp~n"
+ "** Reason for termination = ~n** ~tp~n",
format(Str, [Name, get_msg(Msg), StateName, StateData, Reason1]),
sys:print_log(Debug),
ok.
get_msg_str({'$gen_event', _Event}) ->
- "** Last event in was ~p~n";
+ "** Last event in was ~tp~n";
get_msg_str({'$gen_sync_event', _Event}) ->
- "** Last sync event in was ~p~n";
+ "** Last sync event in was ~tp~n";
get_msg_str({'$gen_all_state_event', _Event}) ->
- "** Last event in was ~p (for all states)~n";
+ "** Last event in was ~tp (for all states)~n";
get_msg_str({'$gen_sync_all_state_event', _Event}) ->
- "** Last sync event in was ~p (for all states)~n";
+ "** Last sync event in was ~tp (for all states)~n";
get_msg_str({timeout, _Ref, {'$gen_timer', _Msg}}) ->
- "** Last timer event in was ~p~n";
+ "** Last timer event in was ~tp~n";
get_msg_str({timeout, _Ref, {'$gen_event', _Msg}}) ->
- "** Last timer event in was ~p~n";
+ "** Last timer event in was ~tp~n";
get_msg_str(_Msg) ->
- "** Last message in was ~p~n".
+ "** Last message in was ~tp~n".
get_msg({'$gen_event', Event}) -> Event;
get_msg({'$gen_sync_event', Event}) -> Event;
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index a3d53efd0d..7daa7a9fe4 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -621,7 +621,7 @@ try_dispatch(Mod, Func, Msg, State) ->
case erlang:function_exported(Mod, handle_info, 2) of
false ->
error_logger:warning_msg("** Undefined handle_info in ~p~n"
- "** Unhandled message: ~p~n",
+ "** Unhandled message: ~tp~n",
[Mod, Msg]),
{ok, {noreply, State}};
true ->
@@ -785,21 +785,21 @@ system_replace_state(StateFun, [Name, State, Mod, Time, HibernateAfterTimeout])
print_event(Dev, {in, Msg}, Name) ->
case Msg of
{'$gen_call', {From, _Tag}, Call} ->
- io:format(Dev, "*DBG* ~p got call ~p from ~w~n",
+ io:format(Dev, "*DBG* ~tp got call ~tp from ~w~n",
[Name, Call, From]);
{'$gen_cast', Cast} ->
- io:format(Dev, "*DBG* ~p got cast ~p~n",
+ io:format(Dev, "*DBG* ~tp got cast ~tp~n",
[Name, Cast]);
_ ->
- io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
+ io:format(Dev, "*DBG* ~tp got ~tp~n", [Name, Msg])
end;
print_event(Dev, {out, Msg, To, State}, Name) ->
- io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n",
+ io:format(Dev, "*DBG* ~tp sent ~tp to ~w, new state ~tp~n",
[Name, Msg, To, State]);
print_event(Dev, {noreply, State}, Name) ->
- io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]);
+ io:format(Dev, "*DBG* ~tp new state ~tp~n", [Name, State]);
print_event(Dev, Event, Name) ->
- io:format(Dev, "*DBG* ~p dbg ~p~n", [Name, Event]).
+ io:format(Dev, "*DBG* ~tp dbg ~tp~n", [Name, Event]).
%%% ---------------------------------------------------
@@ -881,10 +881,10 @@ error_info(Reason, Name, From, Msg, State, Debug) ->
end,
{ClientFmt, ClientArgs} = client_stacktrace(From),
LimitedState = error_logger:limit_term(State),
- error_logger:format("** Generic server ~p terminating \n"
- "** Last message in was ~p~n"
- "** When Server state == ~p~n"
- "** Reason for termination == ~n** ~p~n" ++ ClientFmt,
+ error_logger:format("** Generic server ~tp terminating \n"
+ "** Last message in was ~tp~n"
+ "** When Server state == ~tp~n"
+ "** Reason for termination == ~n** ~tp~n" ++ ClientFmt,
[Name, Msg, LimitedState, Reason1] ++ ClientArgs),
sys:print_log(Debug),
ok.
@@ -898,11 +898,11 @@ client_stacktrace(From) when is_pid(From), node(From) =:= node() ->
{"** Client ~p is dead~n", [From]};
[{current_stacktrace, Stacktrace}, {registered_name, []}] ->
{"** Client ~p stacktrace~n"
- "** ~p~n",
+ "** ~tp~n",
[From, Stacktrace]};
[{current_stacktrace, Stacktrace}, {registered_name, Name}] ->
- {"** Client ~p stacktrace~n"
- "** ~p~n",
+ {"** Client ~tp stacktrace~n"
+ "** ~tp~n",
[Name, Stacktrace]}
end;
client_stacktrace(From) when is_pid(From) ->
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index b5e9da1e66..1110d18af6 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -791,34 +791,34 @@ format_status(
print_event(Dev, {in,Event}, {Name,State}) ->
io:format(
- Dev, "*DBG* ~p receive ~s in state ~p~n",
+ Dev, "*DBG* ~tp receive ~ts in state ~tp~n",
[Name,event_string(Event),State]);
print_event(Dev, {out,Reply,{To,_Tag}}, {Name,State}) ->
io:format(
- Dev, "*DBG* ~p send ~p to ~p from state ~p~n",
+ Dev, "*DBG* ~tp send ~tp to ~p from state ~tp~n",
[Name,Reply,To,State]);
print_event(Dev, {terminate,Reason}, {Name,State}) ->
io:format(
- Dev, "*DBG* ~p terminate ~p in state ~p~n",
+ Dev, "*DBG* ~tp terminate ~tp in state ~tp~n",
[Name,Reason,State]);
print_event(Dev, {Tag,Event,NextState}, {Name,State}) ->
StateString =
case NextState of
State ->
- io_lib:format("~p", [State]);
+ io_lib:format("~tp", [State]);
_ ->
- io_lib:format("~p => ~p", [State,NextState])
+ io_lib:format("~tp => ~tp", [State,NextState])
end,
io:format(
- Dev, "*DBG* ~p ~w ~s in state ~s~n",
+ Dev, "*DBG* ~tp ~tw ~ts in state ~ts~n",
[Name,Tag,event_string(Event),StateString]).
event_string(Event) ->
case Event of
{{call,{Pid,_Tag}},Request} ->
- io_lib:format("call ~p from ~w", [Request,Pid]);
+ io_lib:format("call ~tp from ~w", [Request,Pid]);
{EventType,EventContent} ->
- io_lib:format("~w ~p", [EventType,EventContent])
+ io_lib:format("~tw ~tp", [EventType,EventContent])
end.
sys_debug(Debug, #{name := Name}, State, Entry) ->
@@ -1732,25 +1732,25 @@ error_info(
CallbackMode
end,
error_logger:format(
- "** State machine ~p terminating~n" ++
+ "** State machine ~tp terminating~n" ++
case Q of
[] -> "";
- _ -> "** Last event = ~p~n"
+ _ -> "** Last event = ~tp~n"
end ++
- "** When server state = ~p~n" ++
- "** Reason for termination = ~w:~p~n" ++
+ "** When server state = ~tp~n" ++
+ "** Reason for termination = ~w:~tp~n" ++
"** Callback mode = ~p~n" ++
case Q of
- [_,_|_] -> "** Queued = ~p~n";
+ [_,_|_] -> "** Queued = ~tp~n";
_ -> ""
end ++
case P of
[] -> "";
- _ -> "** Postponed = ~p~n"
+ _ -> "** Postponed = ~tp~n"
end ++
case FixedStacktrace of
[] -> "";
- _ -> "** Stacktrace =~n** ~p~n"
+ _ -> "** Stacktrace =~n** ~tp~n"
end,
[Name |
case Q of
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index d4d1bdccec..8e10cbe93b 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -823,22 +823,22 @@ to_string(A, _) ->
io_lib:write_atom(A).
pp_fun({Enc,Depth}) ->
- {Letter,Tl} = case Depth of
- unlimited -> {"p",[]};
- _ -> {"P",[Depth]}
- end,
- P = modifier(Enc) ++ Letter,
+ {P,Tl} = p(Enc, Depth),
fun(Term, I) ->
io_lib:format("~." ++ integer_to_list(I) ++ P, [Term|Tl])
end.
-format_tag(Indent, Tag, Data, {_Enc,Depth}) ->
- case Depth of
- unlimited ->
- io_lib:format("~s~p: ~80.18p~n", [Indent, Tag, Data]);
- _ ->
- io_lib:format("~s~p: ~80.18P~n", [Indent, Tag, Data, Depth])
- end.
+format_tag(Indent, Tag, Data, {Enc,Depth}) ->
+ {P,Tl} = p(Enc, Depth),
+ io_lib:format("~s~p: ~80.18" ++ P ++ "\n", [Indent, Tag, Data|Tl]).
+
+p(Encoding, Depth) ->
+ {Letter, Tl} = case Depth of
+ unlimited -> {"p", []};
+ _ -> {"P", [Depth]}
+ end,
+ P = modifier(Encoding) ++ Letter,
+ {P, Tl}.
modifier(latin1) -> "";
modifier(_) -> "t".
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index 535ca57a6b..f11f9d0a0b 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -1132,7 +1132,7 @@ wait_for_request(Parent, MonRef, Post) ->
wait_for_request(Parent, MonRef, Post);
Other ->
error_logger:error_msg(
- "The qlc cursor ~w received an unexpected message:\n~p\n",
+ "The qlc cursor ~w received an unexpected message:\n~tp\n",
[self(), Other]),
wait_for_request(Parent, MonRef, Post)
end.
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 26b3960f4f..212b143b1d 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -1459,7 +1459,7 @@ check_env(V) ->
{ok, Val} ->
Txt = io_lib:fwrite
("Invalid value of STDLIB configuration parameter"
- "~w: ~tp\n", [V, Val]),
+ "~tw: ~tp\n", [V, Val]),
error_logger:info_report(lists:flatten(Txt))
end.
diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl
index 5b5c328c0c..d7cf6386f5 100644
--- a/lib/stdlib/src/slave.erl
+++ b/lib/stdlib/src/slave.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -77,7 +77,7 @@ start_pseudo(_,_,_) -> ok. %% It's already there
Pid :: pid().
relay({badrpc,Reason}) ->
- error_msg(" ** exiting relay server ~w :~w **~n", [self(),Reason]),
+ error_msg(" ** exiting relay server ~w :~tw **~n", [self(),Reason]),
exit(Reason);
relay(undefined) ->
error_msg(" ** exiting relay server ~w **~n", [self()]),
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 1cd65fbf18..7920e55930 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -628,7 +628,7 @@ handle_info({'EXIT', Pid, Reason}, State) ->
end;
handle_info(Msg, State) ->
- error_logger:error_msg("Supervisor received unexpected message: ~p~n",
+ error_logger:error_msg("Supervisor received unexpected message: ~tp~n",
[Msg]),
{noreply, State}.
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index 72211332e9..7b79dcf04d 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -36,6 +36,7 @@ MODULES= \
ets_tough_SUITE \
expand_test \
expand_test1 \
+ unicode_expand \
ExpandTestCaps \
ExpandTestCaps1 \
filelib_SUITE \
diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl
index 1f694ea549..5c2b1965ba 100644
--- a/lib/stdlib/test/edlin_expand_SUITE.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE.erl
@@ -22,7 +22,7 @@
init_per_testcase/2, end_per_testcase/2,
init_per_group/2,end_per_group/2]).
-export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1, erl_1152/1,
- erl_352/1]).
+ erl_352/1, unicode/1]).
-include_lib("common_test/include/ct.hrl").
@@ -37,7 +37,8 @@ suite() ->
{timetrap,{minutes,1}}].
all() ->
- [normal, quoted_fun, quoted_module, quoted_both, erl_1152, erl_352].
+ [normal, quoted_fun, quoted_module, quoted_both, erl_1152, erl_352,
+ unicode].
groups() ->
[].
@@ -150,6 +151,7 @@ quoted_both(Config) when is_list(Config) ->
{yes,"weird-fun-name'()",[]} = do_expand("'ExpandTestCaps1':'#"),
ok.
+%% Note: pull request #1152.
erl_1152(Config) when is_list(Config) ->
"\n"++"foo"++" "++[1089]++_ = do_format(["foo",[1089]]),
ok.
@@ -226,6 +228,26 @@ check_trailing([I|Str], ArityStr, Suffix, Dots) ->
Rest =:= Suffix
end.
+unicode(Config) when is_list(Config) ->
+ {module,unicode_expand} = c:l('unicode_expand'),
+ {no,[],[{"'кlирилли́ческий атом'",0},
+ {"'кlирилли́ческий атом'",1},
+ {"'кlирилли́ческий атомB'",1},
+ {"module_info",0},
+ {"module_info",1}]} = do_expand("unicode_expand:"),
+ {yes,"рилли́ческий атом", []} = do_expand("unicode_expand:'кlи"),
+ {yes,"еский атом", []} = do_expand("unicode_expand:'кlирилли́ч"),
+ {yes,"(",[]} = do_expand("unicode_expand:'кlирилли́ческий атомB'"),
+ "\n'кlирилли́ческий атом'/0 'кlирилли́ческий атом'/1 "
+ "'кlирилли́ческий атомB'/1 \nmodule_info/0 "
+ "module_info/1 \n" =
+ do_format([{"'кlирилли́ческий атом'",0},
+ {"'кlирилли́ческий атом'",1},
+ {"'кlирилли́ческий атомB'",1},
+ {"module_info",0},
+ {"module_info",1}]),
+ ok.
+
do_expand(String) ->
edlin_expand:expand(lists:reverse(String)).
diff --git a/lib/stdlib/test/error_logger_h_SUITE.erl b/lib/stdlib/test/error_logger_h_SUITE.erl
index 30f96e0522..1f2a9fda0b 100644
--- a/lib/stdlib/test/error_logger_h_SUITE.erl
+++ b/lib/stdlib/test/error_logger_h_SUITE.erl
@@ -162,7 +162,7 @@ tty_log_open(Log) ->
{ok,D} -> D;
_ -> unlimited
end,
- error_logger:add_report_handler(?MODULE, {Fd,Depth}),
+ error_logger:add_report_handler(?MODULE, {Fd,Depth,latin1}),
Fd.
tty_log_close() ->
@@ -393,11 +393,11 @@ dl_format_1([], [], _, Facc, Aacc) ->
%%% calling error_logger_tty_h:write_event/2.
%%%
-init({_,_}=St) ->
+init({_,_,_}=St) ->
{ok,St}.
-handle_event(Event, {Fd,Depth}=St) ->
- case error_logger_tty_h:write_event(tag_event(Event), io_lib, Depth) of
+handle_event(Event, {Fd,Depth,Enc}=St) ->
+ case error_logger_tty_h:write_event(tag_event(Event), io_lib, {Depth,Enc}) of
ok ->
ok;
Str when is_list(Str) ->
@@ -405,7 +405,7 @@ handle_event(Event, {Fd,Depth}=St) ->
end,
{ok,St}.
-terminate(_Reason, {Fd,_}) ->
+terminate(_Reason, {Fd,_,_}) ->
ok = file:close(Fd),
[].
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index c4fafe82a4..7686889360 100644
--- a/lib/stdlib/test/proc_lib_SUITE.erl
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -552,14 +552,17 @@ t_format(_Config) ->
t_format() ->
error_logger:add_report_handler(?MODULE, self()),
- Pid = proc_lib:spawn(fun t_format_looper/0),
+ Pid = proc_lib:spawn(fun '\x{aaa}t_format_looper'/0),
HugeData = gb_sets:from_list(lists:seq(1, 100)),
- Pid ! {die,HugeData},
+ SomeData1 = list_to_atom([246]),
+ SomeData2 = list_to_atom([1024]),
+ Pid ! {SomeData1,SomeData2},
+ Pid ! {die,{HugeData,SomeData1,SomeData2}},
Report = receive
{crash_report, Pid, Report0} -> Report0
end,
- Usz = do_test_format(Report, unlimited),
- Tsz = do_test_format(Report, 20),
+ Usz = do_test_format(Report, latin1, unlimited),
+ Tsz = do_test_format(Report, latin1, 20),
if
Tsz >= Usz ->
@@ -568,6 +571,16 @@ t_format() ->
ok
end,
+ UszU = do_test_format(Report, unicode, unlimited),
+ TszU = do_test_format(Report, unicode, 20),
+
+ if
+ TszU >= UszU ->
+ ct:fail(failed);
+ true ->
+ ok
+ end,
+
ok.
t_format_arbitrary(_Config) ->
@@ -597,15 +610,19 @@ do_test_format(Report, Encoding, Depth) ->
io:format("*** Depth = ~p, Encoding = ~p", [Depth, Encoding]),
S0 = proc_lib:format(Report, Encoding, Depth),
S = lists:flatten(S0),
- io:put_chars(S),
+ case Encoding of
+ latin1 -> io:format("~s\n", [S]);
+ _ -> io:format("~ts\n", [S])
+ end,
length(S).
-t_format_looper() ->
+'\x{aaa}t_format_looper'() ->
receive
{die,Data} ->
exit(Data);
- _ ->
- t_format_looper()
+ M ->
+ put(M, M),
+ '\x{aaa}t_format_looper'()
end.
%%-----------------------------------------------------------------
diff --git a/lib/stdlib/test/unicode_expand.erl b/lib/stdlib/test/unicode_expand.erl
new file mode 100644
index 0000000000..41f741fa84
--- /dev/null
+++ b/lib/stdlib/test/unicode_expand.erl
@@ -0,0 +1,36 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(unicode_expand).
+
+-export(['кlирилли́ческий атом'/0, 'кlирилли́ческий атом'/1,
+ 'кlирилли́ческий атомB'/1]).
+
+-export_type(['кlирилли́ческий атом'/0]).
+
+-type 'кlирилли́ческий атом'() :: integer().
+
+'кlирилли́ческий атом'() ->
+ 'кlирилли́ческий атом'('кlирилли́ческий атом').
+
+'кlирилли́ческий атом'(_Atom) ->
+ ok.
+
+'кlирилли́ческий атомB'(_B) ->
+ true.
diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl
index cf1ba0abfa..0a12e8fd8b 100644
--- a/lib/syntax_tools/src/epp_dodger.erl
+++ b/lib/syntax_tools/src/epp_dodger.erl
@@ -866,10 +866,10 @@ tokens_to_string([]) ->
format_error(macro_args) ->
errormsg("macro call missing end parenthesis");
format_error({unknown, Reason}) ->
- errormsg(io_lib:format("unknown error: ~P", [Reason, 15])).
+ errormsg(io_lib:format("unknown error: ~tP", [Reason, 15])).
errormsg(String) ->
- io_lib:format("~s: ~s", [?MODULE, String]).
+ io_lib:format("~s: ~ts", [?MODULE, String]).
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl
index a7a2c10b79..07e501e553 100644
--- a/lib/syntax_tools/src/erl_comment_scan.erl
+++ b/lib/syntax_tools/src/erl_comment_scan.erl
@@ -309,7 +309,7 @@ filename([C|T]) when is_integer(C), C > 0 ->
filename([]) ->
[];
filename(N) ->
- report_error("bad filename: `~P'.", [N, 25]),
+ report_error("bad filename: `~tP'.", [N, 25]),
exit(error).
error_read_file(Name) ->
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 888cb71f51..5623aa6af3 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -193,7 +193,7 @@ dir_3(Name, Dir, Regexp, Env) ->
dir_1(Dir1, Regexp, Env).
dir_4(File, Regexp, Env) ->
- case re:run(File, Regexp) of
+ case re:run(File, Regexp, [unicode]) of
{match, _} ->
Opts = [{outfile, File}, {dir, ""} | Env#dir.options],
case catch file(File, Opts) of
@@ -805,7 +805,7 @@ keep_form(Form, Used, Opts) ->
{F, A} = N,
File = proplists:get_value(file, Opts, ""),
report({File, erl_syntax:get_pos(Form),
- "removing unused function `~w/~w'."},
+ "removing unused function `~tw/~w'."},
[F, A], Opts),
false;
true ->
@@ -870,8 +870,8 @@ update_attribute(F, Imports, Opts) ->
Names ->
File = proplists:get_value(file, Opts, ""),
report({File, erl_syntax:get_pos(F),
- "removing unused imports:~s"},
- [[io_lib:fwrite("\n\t`~w:~w/~w'", [M, N, A])
+ "removing unused imports:~ts"},
+ [[io_lib:fwrite("\n\t`~w:~tw/~w'", [M, N, A])
|| {N, A} <- Names]], Opts)
end,
Is = [make_fname(N) || N <- Ns1],
@@ -1166,7 +1166,7 @@ visit_import_application({N, A} = Name, F, As, Tree, Env, St0) ->
case Expand of
true ->
report({Env#env.file, erl_syntax:get_pos(F),
- "expanding call to imported function `~w:~w/~w'."},
+ "expanding call to imported function `~w:~tw/~w'."},
[M, N, A], Env#env.verbosity),
F1 = erl_syntax:module_qualifier(erl_syntax:atom(M),
erl_syntax:atom(N)),
@@ -1220,7 +1220,7 @@ visit_spawn_call({N, A}, F, Ps, [A1, A2, A3] = As, Tree,
case erl_syntax:is_proper_list(A3) of
true ->
report({Env#env.file, erl_syntax:get_pos(F),
- "changing use of `~w/~w' to `~w/~w' with a fun."},
+ "changing use of `~tw/~w' to `~tw/~w' with a fun."},
[N, A, N, 1 + length(Ps)], Env#env.verbosity),
F1 = case erl_syntax:is_atom(A1, Env#env.module) of
true ->
@@ -1404,8 +1404,8 @@ visit_remote_application({M, N, A} = Name, F, As, Tree, Env, St) ->
case rename_remote_call(Name, St) of
{M1, N1} ->
report({Env#env.file, erl_syntax:get_pos(F),
- "updating obsolete call to `~w:~w/~w' "
- "to use `~w:~w/~w' instead."},
+ "updating obsolete call to `~w:~tw/~w' "
+ "to use `~w:~tw/~w' instead."},
[M, N, A, M1, N1, A], Env#env.verbosity),
M2 = erl_syntax:atom(M1),
N2 = erl_syntax:atom(N1),
@@ -1820,7 +1820,7 @@ filename([]) ->
filename(N) when is_atom(N) ->
atom_to_list(N);
filename(N) ->
- report_error("bad filename: `~P'.", [N, 25]),
+ report_error("bad filename: `~tP'.", [N, 25]),
exit(error).
get_env(Tree) ->
@@ -1911,11 +1911,11 @@ format({warning, D}, Vs) ->
format({recommend, D}, Vs) ->
["recommendation: ", format(D, Vs)];
format({"", L, D}, Vs) when is_integer(L), L > 0 ->
- [io_lib:fwrite("~w: ", [L]), format(D, Vs)];
+ [io_lib:fwrite("~tw: ", [L]), format(D, Vs)];
format({"", _L, D}, Vs) ->
format(D, Vs);
format({F, L, D}, Vs) when is_integer(L), L > 0 ->
- [io_lib:fwrite("~ts:~w: ", [filename(F), L]), format(D, Vs)];
+ [io_lib:fwrite("~ts:~tw: ", [filename(F), L]), format(D, Vs)];
format({F, _L, D}, Vs) ->
[io_lib:fwrite("~ts: ", [filename(F)]), format(D, Vs)];
format(S, Vs) when is_list(S) ->
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index b92cd8d607..16e3511734 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -834,7 +834,7 @@ merge_sources_1(Name, Modules, Trees, Opts) ->
dict:from_list(Rs);
false ->
report_error("bad value for `redirect' option: "
- "~P.",
+ "~tP.",
[Rs, 10]),
exit(error)
end,
@@ -1069,7 +1069,7 @@ filter_forms_2(Forms, Env) ->
comment -> kill;
_ ->
report_error("invalid value for option "
- "`file_attributes': ~w.",
+ "`file_attributes': ~tw.",
[FileAttrsOpt]),
exit(error)
end,
@@ -1180,7 +1180,7 @@ merge_namespaces(Modules, Env) ->
[] ->
ok;
Fs ->
- report_warning("interface functions renamed:\n\t~p.", [Fs])
+ report_warning("interface functions renamed:\n\t~tp.", [Fs])
end,
{M4, Acc2} = merge_namespaces_1(M2, Acc1),
Ms = M3 ++ M4,
@@ -1778,7 +1778,7 @@ transform_function(T, Env, St) ->
{maybe_modified(V, T1, 2, Text, Env), St1}.
renaming_note(Name) ->
- [lists:flatten(io_lib:fwrite("renamed function to `~w'",
+ [lists:flatten(io_lib:fwrite("renamed function to `~tw'",
[Name]))].
rename_atom(Node, Atom) ->
@@ -2488,7 +2488,7 @@ rename(Files, Renamings, Opts) ->
true ->
dict:from_list(Renamings);
false ->
- report_error("bad module renaming: ~P.",
+ report_error("bad module renaming: ~tP.",
[Renamings, 10]),
exit(error)
end,
@@ -2672,7 +2672,7 @@ error_text(D, Name) ->
end.
error_text_1(D, Name) ->
- io_lib:fwrite("error: `~w', ~P.", [Name, D, 15]).
+ io_lib:fwrite("error: `~w', ~tP.", [Name, D, 15]).
check_records(Rs, Name) ->
case duplicates([N || {N, _} <- Rs]) of
@@ -2680,7 +2680,7 @@ check_records(Rs, Name) ->
ok;
Ns ->
report_error("in module `~w': "
- "multiply defined records: ~p.",
+ "multiply defined records: ~tp.",
[Name, Ns]),
exit(error)
end.
@@ -2694,7 +2694,7 @@ expand_imports(Is, Name) ->
ordsets:from_list(As);
Ns ->
report_error("in module `~w': "
- "multiply imported functions: ~p.",
+ "multiply imported functions: ~tp.",
[Name, Ns]),
exit(error)
end.
@@ -2968,7 +2968,7 @@ filename([]) ->
filename(N) when is_atom(N) ->
atom_to_list(N);
filename(N) ->
- report_error("bad filename: `~P'.", [N, 25]),
+ report_error("bad filename: `~tP'.", [N, 25]),
exit(error).
duplicates(Xs) ->
@@ -3031,7 +3031,7 @@ split_lines_1(Cs, Cs1, Ls) ->
%% Reporting
warning_unsafe_call(Name, Module, Target) ->
- report_warning("call to `~w' in module `~w' "
+ report_warning("call to `~tw' in module `~w' "
"possibly unsafe in `~s'.", [Name, Module, Target]).
warning_apply_2(Module, Target) ->
diff --git a/lib/syntax_tools/src/merl.erl b/lib/syntax_tools/src/merl.erl
index d6cf208998..b503944442 100644
--- a/lib/syntax_tools/src/merl.erl
+++ b/lib/syntax_tools/src/merl.erl
@@ -565,13 +565,13 @@ parse_5(Ts, Es) ->
-dialyzer({nowarn_function, parse_error/1}). % no local return
parse_error({L, M, R}) when is_atom(M), is_integer(L) ->
- fail("~w: ~s", [L, M:format_error(R)]);
+ fail("~w: ~ts", [L, M:format_error(R)]);
parse_error({{L,C}, M, R}) when is_atom(M), is_integer(L), is_integer(C) ->
- fail("~w:~w: ~s", [L,C,M:format_error(R)]);
+ fail("~w:~w: ~ts", [L,C,M:format_error(R)]);
parse_error({_, M, R}) when is_atom(M) ->
fail(M:format_error(R));
parse_error(R) ->
- fail("unknown parse error: ~p", [R]).
+ fail("unknown parse error: ~tp", [R]).
%% ------------------------------------------------------------------------
%% Templates, substitution and matching
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml
index 922c2ac0f4..5bdfc60448 100644
--- a/lib/tools/doc/src/lcnt.xml
+++ b/lib/tools/doc/src/lcnt.xml
@@ -471,7 +471,7 @@
<c>lcnt:rt_opt/2</c>.
</p>
<p>Valid categories are:</p>
- <taglist>
+ <list>
<item><c>allocator</c></item>
<item><c>db</c> (ETS tables)</item>
<item><c>debug</c></item>
@@ -480,7 +480,7 @@
<item><c>io</c></item>
<item><c>process</c></item>
<item><c>scheduler</c></item>
- </taglist>
+ </list>
<p>
This list is subject to change at any time, as is the category any given lock
may belong to.
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 012de479d3..9a3985541b 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -931,6 +931,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"has_prepared_code_on_load"
"hibernate"
"insert_element"
+ "iolist_to_iovec"
"is_builtin"
"load_nif"
"loaded"
diff --git a/lib/tools/src/eprof.erl b/lib/tools/src/eprof.erl
index 3ae899a078..535ddbcd04 100644
--- a/lib/tools/src/eprof.erl
+++ b/lib/tools/src/eprof.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -246,7 +246,7 @@ handle_call(profile_stop, _From, #state{ profiling = true } = S) ->
%% logfile
handle_call({logfile, File}, _From, #state{ fd = OldFd } = S) ->
- case file:open(File, [write]) of
+ case file:open(File, [write, {encoding, utf8}]) of
{ok, Fd} ->
case OldFd of
undefined -> ok;
@@ -478,11 +478,11 @@ string_bp_mfa([{Mfa, {Count, Time}}|Mfas], Tus, {MfaW, CountW, PercW, TimeW, TpC
Stpc = s("~.2f", [divide(Time,Count)]),
string_bp_mfa(Mfas, Tus, {
- erlang:max(MfaW, length(Smfa)),
- erlang:max(CountW,length(Scount)),
- erlang:max(PercW, length(Sperc)),
- erlang:max(TimeW, length(Stime)),
- erlang:max(TpCW, length(Stpc))
+ erlang:max(MfaW, string:length(Smfa)),
+ erlang:max(CountW,string:length(Scount)),
+ erlang:max(PercW, string:length(Sperc)),
+ erlang:max(TimeW, string:length(Stime)),
+ erlang:max(TpCW, string:length(Stpc))
}, [[Smfa, Scount, Sperc, Stime, Stpc] | Strings]).
print_bp_mfa(Mfas, {Tn, Tus}, Fd, Opts) ->
@@ -491,11 +491,11 @@ print_bp_mfa(Mfas, {Tn, Tus}, Fd, Opts) ->
TnStr = s(Tn),
TusStr = s(Tus),
TuspcStr = s("~.2f", [divide(Tus,Tn)]),
- Ws = {erlang:max(length("FUNCTION"), MfaW),
- lists:max([length("CALLS"), CountW, length(TnStr)]),
- erlang:max(length(" %"), PercW),
- lists:max([length("TIME"), TimeW, length(TusStr)]),
- lists:max([length("uS / CALLS"), TpCW, length(TuspcStr)])},
+ Ws = {erlang:max(string:length("FUNCTION"), MfaW),
+ lists:max([string:length("CALLS"), CountW, string:length(TnStr)]),
+ erlang:max(string:length(" %"), PercW),
+ lists:max([string:length("TIME"), TimeW, string:length(TusStr)]),
+ lists:max([string:length("uS / CALLS"), TpCW, string:length(TuspcStr)])},
format(Fd, Ws, ["FUNCTION", "CALLS", " %", "TIME", "uS / CALLS"]),
format(Fd, Ws, ["--------", "-----", "-------", "----", "----------"]),
lists:foreach(fun (String) -> format(Fd, Ws, String) end, Strs),
@@ -503,13 +503,13 @@ print_bp_mfa(Mfas, {Tn, Tus}, Fd, Opts) ->
format(Fd, Ws, ["Total:", TnStr, "100.00%", TusStr, TuspcStr]),
ok.
-s({M,F,A}) -> s("~w:~w/~w",[M,F,A]);
-s(Term) -> s("~p", [Term]).
+s({M,F,A}) -> s("~w:~tw/~w",[M,F,A]);
+s(Term) -> s("~tp", [Term]).
s(Format, Terms) -> lists:flatten(io_lib:format(Format, Terms)).
format(Fd, {MfaW, CountW, PercW, TimeW, TpCW}, Strings) ->
- format(Fd, s("~~.~ps ~~~ps ~~~ps ~~~ps [~~~ps]~~n", [MfaW, CountW, PercW, TimeW, TpCW]), Strings);
+ format(Fd, s("~~.~wts ~~~ws ~~~ws ~~~ws [~~~ws]~~n", [MfaW, CountW, PercW, TimeW, TpCW]), Strings);
format(undefined, Format, Strings) ->
io:format(Format, Strings),
ok;
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index 436f68d12b..2fe42beb03 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1475,7 +1475,7 @@ info_suspect_call(GroupLeader, GroupLeader, _, _) ->
ok;
info_suspect_call(GroupLeader, _, Func, Pid) ->
io:format(GroupLeader,
- "~nWarning: ~p called in ~p - trace may become corrupt!~n",
+ "~nWarning: ~tp called in ~p - trace may become corrupt!~n",
parsify([Func, Pid])).
info(GroupLeader, GroupLeader, _, _) ->
@@ -1498,13 +1498,13 @@ dump_stack(Dump, Stack, Term) ->
{N, length(hd(Stack))}
end
end,
- io:format(Dump, "~s~p.~n", [lists:duplicate(Depth, " "), parsify(Term)]),
+ io:format(Dump, "~s~tp.~n", [lists:duplicate(Depth, " "), parsify(Term)]),
true.
dump(undefined, _) ->
false;
dump(Dump, Term) ->
- io:format(Dump, "~p.~n", [parsify(Term)]),
+ io:format(Dump, "~tp.~n", [parsify(Term)]),
true.
@@ -2603,17 +2603,17 @@ println({Io, [W1, W2, W3, W4]}, Head,
println({Io, _}, Head,
[],
Tail, Comment) ->
- io:format(Io, "~s~s~s~n",
+ io:format(Io, "~s~ts~ts~n",
[pad(Head, $ , 3), Tail, Comment]);
println({Io, _}, Head,
{Tag, Term},
Tail, Comment) ->
- io:format(Io, "~s~p, ~p~s~s~n",
+ io:format(Io, "~s~tp, ~tp~ts~ts~n",
[pad(Head, $ , 3), parsify(Tag), parsify(Term), Tail, Comment]);
println({Io, _}, Head,
Term,
Tail, Comment) ->
- io:format(Io, "~s~p~s~s~n",
+ io:format(Io, "~s~tp~ts~ts~n",
[pad(Head, $ , 3), parsify(Term), Tail, Comment]).
@@ -2720,7 +2720,7 @@ postsort_r([[_|C] | L], R) ->
flat_format(F, Trailer) when is_float(F) ->
lists:flatten([io_lib:format("~.3f", [F]), Trailer]);
flat_format(W, Trailer) ->
- lists:flatten([io_lib:format("~p", [W]), Trailer]).
+ lists:flatten([io_lib:format("~tp", [W]), Trailer]).
%% Format, flatten, and pad.
flat_format(Term, Trailer, Width) ->
diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl
index 8d2cc07e40..3199b28acb 100644
--- a/lib/tools/src/xref_base.erl
+++ b/lib/tools/src/xref_base.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -405,21 +405,21 @@ analysis(exports_not_used, _) ->
%% Local calls are not considered here. "X * UU" would do otherwise.
"X - XU";
analysis({call, F}, functions) ->
- make_query("range (E | ~w : Fun)", [F]);
+ make_query("range (E | ~tw : Fun)", [F]);
analysis({use, F}, functions) ->
- make_query("domain (E || ~w : Fun)", [F]);
+ make_query("domain (E || ~tw : Fun)", [F]);
analysis({module_call, M}, _) ->
- make_query("range (ME | ~w : Mod)", [M]);
+ make_query("range (ME | ~tw : Mod)", [M]);
analysis({module_use, M}, _) ->
- make_query("domain (ME || ~w : Mod)", [M]);
+ make_query("domain (ME || ~tw : Mod)", [M]);
analysis({application_call, A}, _) ->
- make_query("range (AE | ~w : App)", [A]);
+ make_query("range (AE | ~tw : App)", [A]);
analysis({application_use, A}, _) ->
- make_query("domain (AE || ~w : App)", [A]);
+ make_query("domain (AE || ~tw : App)", [A]);
analysis({release_call, R}, _) ->
- make_query("range (RE | ~w : Rel)", [R]);
+ make_query("range (RE | ~tw : Rel)", [R]);
analysis({release_use, R}, _) ->
- make_query("domain (RE || ~w : Rel)", [R]);
+ make_query("domain (RE || ~tw : Rel)", [R]);
analysis(deprecated_function_calls, functions) ->
"XC || DF";
analysis({deprecated_function_calls,Flag}, functions) ->
@@ -1833,9 +1833,9 @@ message(true, What, Arg) ->
unreadable ->
io:format("Skipping ~ts (unreadable)~n", [Arg]);
xref_attr ->
- io:format("~ts: Skipping 'xref' attribute ~w~n", Arg);
+ io:format("~ts: Skipping 'xref' attribute ~tw~n", Arg);
depr_attr ->
- io:format("~ts: Skipping 'deprecated' attribute ~w~n", Arg);
+ io:format("~ts: Skipping 'deprecated' attribute ~tw~n", Arg);
lib_search ->
io:format("Scanning library path for BEAM files... ", []);
lib_check ->
diff --git a/lib/tools/src/xref_parser.yrl b/lib/tools/src/xref_parser.yrl
index 0711da79e2..5ee6419ff5 100644
--- a/lib/tools/src/xref_parser.yrl
+++ b/lib/tools/src/xref_parser.yrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -170,7 +170,7 @@ is_prefix_op('#') -> numeric;
is_prefix_op(_) -> false.
check_regexp(String) ->
- case re:compile(String) of
+ case re:compile(String, [unicode]) of
{ok, _Expr} ->
{regexpr, String};
{error, {ErrString, Position}} ->
@@ -274,7 +274,7 @@ mfa2s({M,F,A}) ->
[c2s(M),':',c2s(F),'/',A].
c2s(C) ->
- [S] = io_lib:format("~p", [C]),
+ [S] = io_lib:format("~tp", [C]),
list_to_atom(S).
re(variable) -> ['_'];
diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl
index b0c168e018..02e207d40c 100644
--- a/lib/tools/src/xref_utils.erl
+++ b/lib/tools/src/xref_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -638,14 +638,14 @@ neighbours([], G, Fun, VT, L, _V, Vs) ->
neighbours(Vs, G, Fun, VT, L).
match_list(L, RExpr) ->
- {ok, Expr} = re:compile(RExpr),
+ {ok, Expr} = re:compile(RExpr, [unicode]),
filter(fun(E) -> match(E, Expr) end, L).
match_one(VarL, Con, Col) ->
select_each(VarL, fun(E) -> Con =:= element(Col, E) end).
match_many(VarL, RExpr, Col) ->
- {ok, Expr} = re:compile(RExpr),
+ {ok, Expr} = re:compile(RExpr, [unicode]),
select_each(VarL, fun(E) -> match(element(Col, E), Expr) end).
match(I, Expr) when is_integer(I) ->
@@ -653,7 +653,12 @@ match(I, Expr) when is_integer(I) ->
{match, [{0,length(S)}]} =:= re:run(S, Expr, [{capture, first}]);
match(A, Expr) when is_atom(A) ->
S = atom_to_list(A),
- {match, [{0,length(S)}]} =:= re:run(S, Expr, [{capture, first}]).
+ case re:run(S, Expr, [{capture, first}]) of
+ {match, [{0,Size}]} ->
+ Size =:= byte_size(unicode:characters_to_binary(S));
+ _ ->
+ false
+ end.
select_each([{Mod,Funs} | L], Pred) ->
case filter(Pred, Funs) of
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index 057449d4a2..379a5c09ab 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -50,7 +50,8 @@
-export([analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]).
--export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1, otp_13708/1]).
+-export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1, otp_13708/1,
+ otp_14464/1]).
-import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]).
@@ -81,8 +82,10 @@ groups() ->
update, deprecated, trycatch, fun_mfa,
fun_mfa_r14, fun_mfa_vars, qlc]},
{analyses, [],
+
[analyze, basic, md, q, variables, unused_locals]},
- {misc, [], [format_error, otp_7423, otp_7831, otp_10192, otp_13708]}].
+ {misc, [], [format_error, otp_7423, otp_7831, otp_10192, otp_13708,
+ otp_14464]}].
init_per_suite(Conf) when is_list(Conf) ->
@@ -2396,7 +2399,6 @@ otp_10192(Conf) when is_list(Conf) ->
xref:stop(s),
ok.
-%% OTP-10192. Allow filenames with character codes greater than 126.
otp_13708(Conf) when is_list(Conf) ->
{ok, _} = start(s),
ok = xref:set_default(s, [{verbose, true}]),
@@ -2409,6 +2411,36 @@ otp_13708(Conf) when is_list(Conf) ->
ok = xref:set_library_path(s, [Dir], [{verbose, true}]),
xref:stop(s).
+%% OTP-14464. Unicode atoms.
+otp_14464(Conf) when is_list(Conf) ->
+ Dir = ?copydir,
+
+ File1 = fname(Dir, "a.erl"),
+ MFile1 = fname(Dir, "a"),
+ Beam1 = fname(Dir, "a.beam"),
+ Test1 = "-module(a).
+ -export([ärlig/0, 'кlирилли́ческий атомB'/0]).
+
+ ärlig() ->
+ 'кlирилли́ческий атомB'.
+
+ 'кlирилли́ческий атомB'() ->
+ foo.
+ ",
+ ok = file:write_file(File1, unicode:characters_to_binary(Test1)),
+ {ok, a} = compile:file(File1, [debug_info,{outdir,Dir}]),
+
+ {ok, _} = xref:start(s),
+ {ok, a} = xref:add_module(s, MFile1),
+
+ {ok, [{a,ärlig,0}]} = xref:q(s, 'a:"ärlig"/0'),
+ {ok, [{a,'кlирилли́ческий атомB',0}]} =
+ xref:q(s, 'a:"кlирилли́ческий атомB"/0'),
+
+ xref:stop(s),
+ ok = file:delete(File1),
+ ok = file:delete(Beam1).
+
%%%
%%% Utilities
%%%
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index 42973335b4..cc19ff9770 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -561,21 +561,21 @@ system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) ->
print_event(Dev, {in, Msg}, Name) ->
case Msg of
{'$gen_call', {From, _Tag}, Call} ->
- io:format(Dev, "*DBG* ~p got call ~p from ~w~n",
+ io:format(Dev, "*DBG* ~tp got call ~tp from ~w~n",
[Name, Call, From]);
{'$gen_cast', Cast} ->
- io:format(Dev, "*DBG* ~p got cast ~p~n",
+ io:format(Dev, "*DBG* ~tp got cast ~tp~n",
[Name, Cast]);
_ ->
- io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
+ io:format(Dev, "*DBG* ~tp got ~tp~n", [Name, Msg])
end;
print_event(Dev, {out, Msg, To, State}, Name) ->
- io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n",
+ io:format(Dev, "*DBG* ~tp sent ~tp to ~w, new state ~tp~n",
[Name, Msg, To, State]);
print_event(Dev, {noreply, State}, Name) ->
- io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]);
+ io:format(Dev, "*DBG* ~tp new state ~tp~n", [Name, State]);
print_event(Dev, Event, Name) ->
- io:format(Dev, "*DBG* ~p dbg ~p~n", [Name, Event]).
+ io:format(Dev, "*DBG* ~tp dbg ~tp~n", [Name, Event]).
%%% ---------------------------------------------------
%%% Terminate the server.
@@ -629,10 +629,10 @@ error_info(Reason, Name, Msg, State, Debug) ->
_ ->
Reason
end,
- format("** wx object server ~p terminating \n"
- "** Last message in was ~p~n"
- "** When Server state == ~p~n"
- "** Reason for termination == ~n** ~p~n",
+ format("** wx object server ~tp terminating \n"
+ "** Last message in was ~tp~n"
+ "** When Server state == ~tp~n"
+ "** Reason for termination == ~n** ~tp~n",
[Name, Msg, State, Reason1]),
sys:print_log(Debug),
ok.
@@ -657,7 +657,7 @@ debug_options(Name, Opts) ->
dbg_opts(Name, Opts) ->
case catch sys:debug_options(Opts) of
{'EXIT',_} ->
- format("~p: ignoring erroneous debug options - ~p~n",
+ format("~tp: ignoring erroneous debug options - ~tp~n",
[Name, Opts]),
[];
Dbg ->