aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/src/asn1rt_nif.erl1
-rw-r--r--lib/crypto/src/crypto.erl1
-rw-r--r--lib/debugger/src/dbg_ieval.erl3
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl5
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c9
-rw-r--r--lib/erl_interface/src/prog/erl_call.c3
-rw-r--r--lib/hipe/arm/hipe_arm_assemble.erl2
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl5
-rw-r--r--lib/hipe/icode/hipe_icode_range.erl20
-rw-r--r--lib/hipe/llvm/hipe_llvm_merge.erl2
-rw-r--r--lib/hipe/misc/hipe_consttab.erl15
-rw-r--r--lib/hipe/misc/hipe_pack_constants.erl12
-rw-r--r--lib/hipe/ppc/hipe_ppc_assemble.erl2
-rw-r--r--lib/hipe/regalloc/hipe_coalescing_regalloc.erl2
-rw-r--r--lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl6
-rw-r--r--lib/hipe/regalloc/hipe_optimistic_regalloc.erl2
-rw-r--r--lib/hipe/sparc/hipe_sparc_assemble.erl2
-rw-r--r--lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl42
-rw-r--r--lib/hipe/x86/hipe_x86_assemble.erl2
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE_data/c_client.c4
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c4
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c4
-rw-r--r--lib/ic/test/erl_client_c_server_SUITE_data/c_server.c4
-rw-r--r--lib/ic/test/erl_client_c_server_proto_SUITE_data/c_server.c4
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml17
-rw-r--r--lib/kernel/doc/src/inet.xml2
-rw-r--r--lib/kernel/src/kernel.erl244
-rw-r--r--lib/observer/src/observer_alloc_wx.erl22
-rw-r--r--lib/observer/src/observer_app_wx.erl10
-rw-r--r--lib/observer/src/observer_lib.erl13
-rw-r--r--lib/observer/src/observer_perf_wx.erl18
-rw-r--r--lib/observer/src/observer_port_wx.erl39
-rw-r--r--lib/observer/src/observer_pro_wx.erl34
-rw-r--r--lib/observer/src/observer_sys_wx.erl13
-rw-r--r--lib/observer/src/observer_trace_wx.erl71
-rw-r--r--lib/observer/src/observer_tv_table.erl30
-rw-r--r--lib/observer/src/observer_tv_wx.erl83
-rw-r--r--lib/observer/src/observer_wx.erl146
-rw-r--r--lib/runtime_tools/src/dyntrace.erl2
-rw-r--r--lib/stdlib/doc/src/assert_hrl.xml15
-rw-r--r--lib/stdlib/doc/src/ets.xml19
-rw-r--r--lib/stdlib/src/ets.erl10
-rw-r--r--lib/stdlib/test/ets_SUITE.erl266
43 files changed, 875 insertions, 335 deletions
diff --git a/lib/asn1/src/asn1rt_nif.erl b/lib/asn1/src/asn1rt_nif.erl
index ff464885f6..e540b9f50d 100644
--- a/lib/asn1/src/asn1rt_nif.erl
+++ b/lib/asn1/src/asn1rt_nif.erl
@@ -26,6 +26,7 @@
decode_ber_tlv/1,
encode_ber_tlv/1]).
+-compile(no_native).
-on_load(load_nif/0).
-define(ASN1_NIF_VSN,1).
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index ce8add6559..d322765dff 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -56,6 +56,7 @@
%%-type ec_curve() :: ec_named_curve() | ec_curve_spec().
%%-type ec_key() :: {Curve :: ec_curve(), PrivKey :: binary() | undefined, PubKey :: ec_point() | undefined}.
+-compile(no_native).
-on_load(on_load/0).
-define(CRYPTO_NIF_VSN,302).
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index f5e079ef7e..88c7caacb0 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.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.
@@ -1486,7 +1486,6 @@ guard_expr({map,_,E0,Fs0}, Bs) ->
Value = lists:foldl(fun ({map_assoc,K,V}, Mi) -> maps:put(K,V,Mi);
({map_exact,K,V}, Mi) -> maps:update(K,V,Mi) end,
E, Fs),
- io:format("~p~n", [{E,Value}]),
{value,Value};
guard_expr({bin,_,Flds}, Bs) ->
{value,V,_Bs} =
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index 29c8e8cefb..f4ee30618c 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.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.
@@ -524,7 +524,8 @@ gui_cmd({edit, {Var, Value}}, State) ->
cancel ->
State;
{Var, Term} ->
- Cmd = atom_to_list(Var)++"="++io_lib:format("~w", [Term]),
+ %% The space after "=" is needed for handling "B= <<1>>".
+ Cmd = atom_to_list(Var)++"= "++io_lib:format("~w", [Term]),
gui_cmd({user_command, lists:flatten(Cmd)}, State)
end.
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index c193fd804a..27b919c093 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -497,7 +497,8 @@ int ei_connect_init(ei_cnode* ec, const char* this_node_name,
}
#endif /* _REENTRANT */
- if (gethostname(thishostname, EI_MAXHOSTNAMELEN) == -1) {
+ /* gethostname requires len to be max(hostname) + 1 */
+ if (gethostname(thishostname, EI_MAXHOSTNAMELEN+1) == -1) {
#ifdef __WIN32__
EI_TRACE_ERR1("ei_connect_init","Failed to get host name: %d",
WSAGetLastError());
@@ -613,7 +614,8 @@ int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned ms)
hp = ei_gethostbyname_r(hostname,&host,buffer,1024,&ei_h_errno);
if (hp == NULL) {
char thishostname[EI_MAXHOSTNAMELEN+1];
- if (gethostname(thishostname,EI_MAXHOSTNAMELEN) < 0) {
+ /* gethostname requies len to be max(hostname) + 1*/
+ if (gethostname(thishostname,EI_MAXHOSTNAMELEN+1) < 0) {
EI_TRACE_ERR0("ei_connect_tmo",
"Failed to get name of this host");
erl_errno = EHOSTUNREACH;
@@ -636,7 +638,8 @@ int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned ms)
#else /* __WIN32__ */
if ((hp = ei_gethostbyname(hostname)) == NULL) {
char thishostname[EI_MAXHOSTNAMELEN+1];
- if (gethostname(thishostname,EI_MAXHOSTNAMELEN) < 0) {
+ /* gethostname requires len to be max(hostname) + 1 */
+ if (gethostname(thishostname,EI_MAXHOSTNAMELEN+1) < 0) {
EI_TRACE_ERR1("ei_connect_tmo",
"Failed to get name of this host: %d",
WSAGetLastError());
diff --git a/lib/erl_interface/src/prog/erl_call.c b/lib/erl_interface/src/prog/erl_call.c
index d233ed26a2..0b09d412db 100644
--- a/lib/erl_interface/src/prog/erl_call.c
+++ b/lib/erl_interface/src/prog/erl_call.c
@@ -325,7 +325,8 @@ int erl_call(int argc, char **argv)
initWinSock();
#endif
- if (gethostname(h_hostname, EI_MAXHOSTNAMELEN) < 0) {
+ /* gethostname requires len to be max(hostname) + 1 */
+ if (gethostname(h_hostname, EI_MAXHOSTNAMELEN+1) < 0) {
fprintf(stderr,"erl_call: failed to get host name: %d\n", errno);
exit(1);
}
diff --git a/lib/hipe/arm/hipe_arm_assemble.erl b/lib/hipe/arm/hipe_arm_assemble.erl
index 713c148742..9aa730afa9 100644
--- a/lib/hipe/arm/hipe_arm_assemble.erl
+++ b/lib/hipe/arm/hipe_arm_assemble.erl
@@ -31,7 +31,7 @@ assemble(CompiledCode, Closures, Exports, Options) ->
|| {MFA, Defun} <- CompiledCode],
%%
{ConstAlign,ConstSize,ConstMap,RefsFromConsts} =
- hipe_pack_constants:pack_constants(Code, 4),
+ hipe_pack_constants:pack_constants(Code),
%%
{CodeSize,CodeBinary,AccRefs,LabelMap,ExportMap} =
encode(translate(Code, ConstMap), Options),
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 8c96e60229..9321750d44 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -2029,17 +2029,14 @@ arith_rem(Min1, Max1, Min2, Max2) ->
Min1_geq_zero = infinity_geq(Min1, 0),
Max1_leq_zero = infinity_geq(0, Max1),
Max_range2 = infinity_max([infinity_abs(Min2), infinity_abs(Max2)]),
- Max_range2_leq_zero = infinity_geq(0, Max_range2),
- New_min =
+ New_min =
if Min1_geq_zero -> 0;
Max_range2 =:= 0 -> 0;
- Max_range2_leq_zero -> infinity_add(Max_range2, 1);
true -> infinity_add(infinity_inv(Max_range2), 1)
end,
New_max =
if Max1_leq_zero -> 0;
Max_range2 =:= 0 -> 0;
- Max_range2_leq_zero -> infinity_add(infinity_inv(Max_range2), -1);
true -> infinity_add(Max_range2, -1)
end,
{New_min, New_max}.
diff --git a/lib/hipe/icode/hipe_icode_range.erl b/lib/hipe/icode/hipe_icode_range.erl
index b884132327..287b1c80fe 100644
--- a/lib/hipe/icode/hipe_icode_range.erl
+++ b/lib/hipe/icode/hipe_icode_range.erl
@@ -392,14 +392,17 @@ widen(#range{range=Old}, #range{range=New}, T = #range{range=Wide}) ->
-spec analyse_call(#icode_call{}, call_fun()) -> #icode_call{}.
analyse_call(Call, LookupFun) ->
+ Args = hipe_icode:args(Call),
+ Fun = hipe_icode:call_fun(Call),
+ Type = hipe_icode:call_type(Call),
+ %% This call has side-effects (it might call LookupFun which sends messages to
+ %% hipe_icode_coordinator to update the argument ranges of Fun), and must thus
+ %% not be moved into the case statement.
+ DstRanges = analyse_call_or_enter_fun(Fun, Args, Type, LookupFun),
case hipe_icode:call_dstlist(Call) of
[] ->
Call;
Dsts ->
- Args = hipe_icode:args(Call),
- Fun = hipe_icode:call_fun(Call),
- Type = hipe_icode:call_type(Call),
- DstRanges = analyse_call_or_enter_fun(Fun, Args, Type, LookupFun),
NewDefs = [update_info(Var, R) || {Var,R} <- lists:zip(Dsts, DstRanges)],
hipe_icode:subst_defines(lists:zip(Dsts, NewDefs), Call)
end.
@@ -1306,16 +1309,15 @@ range_rem(Range1, Range2) ->
Min1_geq_zero = inf_geq(Min1, 0),
Max1_leq_zero = inf_geq(0, Max1),
Max_range2 = inf_max([inf_abs(Min2), inf_abs(Max2)]),
- Max_range2_leq_zero = inf_geq(0, Max_range2),
New_min =
if Min1_geq_zero -> 0;
- Max_range2_leq_zero -> Max_range2;
- true -> inf_inv(Max_range2)
+ Max_range2 =:= 0 -> 0;
+ true -> inf_add(inf_inv(Max_range2), 1)
end,
New_max =
if Max1_leq_zero -> 0;
- Max_range2_leq_zero -> inf_inv(Max_range2);
- true -> Max_range2
+ Max_range2 =:= 0 -> 0;
+ true -> inf_add(Max_range2, -1)
end,
range_init({New_min, New_max}, false).
diff --git a/lib/hipe/llvm/hipe_llvm_merge.erl b/lib/hipe/llvm/hipe_llvm_merge.erl
index 6e891ac3b0..58d862fbb2 100644
--- a/lib/hipe/llvm/hipe_llvm_merge.erl
+++ b/lib/hipe/llvm/hipe_llvm_merge.erl
@@ -13,7 +13,7 @@ finalize(CompiledCode, Closures, Exports) ->
Code = [{MFA, [], ConstTab}
|| {MFA, _, _ , ConstTab, _, _} <- CompiledCode1],
{ConstAlign, ConstSize, ConstMap, RefsFromConsts} =
- hipe_pack_constants:pack_constants(Code, ?ARCH_REGISTERS:alignment()),
+ hipe_pack_constants:pack_constants(Code),
%% Compute total code size separately as a sanity check for alignment
CodeSize = compute_code_size(CompiledCode1, 0),
%% io:format("Code Size (pre-computed): ~w~n", [CodeSize]),
diff --git a/lib/hipe/misc/hipe_consttab.erl b/lib/hipe/misc/hipe_consttab.erl
index 64e3d3ccaa..741bdb2094 100644
--- a/lib/hipe/misc/hipe_consttab.erl
+++ b/lib/hipe/misc/hipe_consttab.erl
@@ -63,9 +63,7 @@
%% A hipe_consttab is a tuple {Data, ReferedLabels, NextConstLabel}
%% @type hipe_constlbl().
%% An abstract datatype for referring to data.
-%% @type element_type() = byte | word | ctab_array()
-%% @type ctab_array() = {ctab_array, Type::element_type(),
-%% NoElements::pos_integer()}
+%% @type element_type() = byte | word
%% @type block() = [integer() | label_ref()]
%% @type label_ref() = {label, Label::code_label()}
%% @type code_label() = hipe_sparc:label_name() | hipe_x86:label_name()
@@ -110,8 +108,7 @@
-type label_ref() :: {'label', code_label()}.
-type block() :: [hipe_constlbl() | label_ref()].
--type ctab_array() :: {'ctab_array', 'byte' | 'word', pos_integer()}.
--type element_type() :: 'byte' | 'word' | ctab_array().
+-type element_type() :: 'byte' | 'word'.
-type sort_order() :: term(). % XXX: FIXME
@@ -187,7 +184,7 @@ insert_block({ConstTab, RefToLabels, NextLabel}, ElementType, InitList) ->
ReferredLabels = get_labels(InitList, []),
NewRefTo = ReferredLabels ++ RefToLabels,
{NewTa, Id} = insert_const({ConstTab, NewRefTo, NextLabel},
- block, word_size(), false,
+ block, size_of(ElementType), false,
{ElementType,InitList}),
{insert_backrefs(NewTa, Id, ReferredLabels), Id}.
@@ -256,13 +253,9 @@ get_labels([], Acc) ->
%% @spec size_of(element_type()) -> pos_integer()
%% @doc Returns the size in bytes of an element_type.
-%% The is_atom/1 guard in the clause handling arrays
-%% constraints the argument to 'byte' | 'word'
-spec size_of(element_type()) -> pos_integer().
size_of(byte) -> 1;
-size_of(word) -> word_size();
-size_of({ctab_array,S,N}) when is_atom(S), is_integer(N), N > 0 ->
- N * size_of(S).
+size_of(word) -> word_size().
%% @spec decompose({element_type(), block()}) -> [byte()]
%% @doc Turns a block into a list of bytes.
diff --git a/lib/hipe/misc/hipe_pack_constants.erl b/lib/hipe/misc/hipe_pack_constants.erl
index 9dd18bce0f..6736d1f503 100644
--- a/lib/hipe/misc/hipe_pack_constants.erl
+++ b/lib/hipe/misc/hipe_pack_constants.erl
@@ -13,7 +13,7 @@
%% limitations under the License.
-module(hipe_pack_constants).
--export([pack_constants/2, slim_refs/1, slim_constmap/1,
+-export([pack_constants/1, slim_refs/1, slim_constmap/1,
find_const/2, mk_data_relocs/2, slim_sorted_exportmap/3]).
-include("hipe_consttab.hrl").
@@ -37,8 +37,8 @@
-record(pcm_entry, {mfa :: mfa(),
label :: hipe_constlbl(),
- const_num :: const_num(),
- start :: addr(),
+ const_num :: const_num(),
+ start :: addr(),
type :: 0 | 1 | 2,
raw_data :: raw_data()}).
-type pcm_entry() :: #pcm_entry{}.
@@ -53,11 +53,11 @@
%%-----------------------------------------------------------------------------
--spec pack_constants([{mfa(),[_],hipe_consttab()}], ct_alignment()) ->
+-spec pack_constants([{mfa(),[_],hipe_consttab()}]) ->
{ct_alignment(), non_neg_integer(), packed_const_map(), mfa_refs_map()}.
-pack_constants(Data, Align) ->
- pack_constants(Data, 0, Align, 0, [], []).
+pack_constants(Data) ->
+ pack_constants(Data, 0, 1, 0, [], []). % 1 = byte alignment
pack_constants([{MFA,_,ConstTab}|Rest], Size, Align, ConstNo, Acc, Refs) ->
Labels = hipe_consttab:labels(ConstTab),
diff --git a/lib/hipe/ppc/hipe_ppc_assemble.erl b/lib/hipe/ppc/hipe_ppc_assemble.erl
index 66817837df..b0f57e5582 100644
--- a/lib/hipe/ppc/hipe_ppc_assemble.erl
+++ b/lib/hipe/ppc/hipe_ppc_assemble.erl
@@ -32,7 +32,7 @@ assemble(CompiledCode, Closures, Exports, Options) ->
|| {MFA, Defun} <- CompiledCode],
%%
{ConstAlign,ConstSize,ConstMap,RefsFromConsts} =
- hipe_pack_constants:pack_constants(Code, hipe_rtl_arch:word_size()),
+ hipe_pack_constants:pack_constants(Code),
%%
{CodeSize,CodeBinary,AccRefs,LabelMap,ExportMap} =
encode(translate(Code, ConstMap), Options),
diff --git a/lib/hipe/regalloc/hipe_coalescing_regalloc.erl b/lib/hipe/regalloc/hipe_coalescing_regalloc.erl
index e8ccbec9f1..b8f0a1974c 100644
--- a/lib/hipe/regalloc/hipe_coalescing_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_coalescing_regalloc.erl
@@ -914,7 +914,7 @@ findCheapest([Node|Nodes], IG, Cost, Cheapest, SpillLimit) ->
%% limit are extremely expensive.
getCost(Node, IG, SpillLimit) ->
- case Node > SpillLimit of
+ case Node >= SpillLimit of
true -> inf;
false -> hipe_ig:node_spill_cost(Node, IG)
end.
diff --git a/lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl b/lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl
index 07aa812f4a..f82d3a2cbc 100644
--- a/lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl
@@ -209,8 +209,8 @@ color(IG, Spill, PhysRegs, SpillIx, SpillLimit, NumNodes, Target,
%% Any nodes above the spillimit must be colored first...
MustNotSpill =
- if NumNodes > SpillLimit+1 ->
- sort_on_degree(lists:seq(SpillLimit+1,NumNodes-1) -- Low,IG);
+ if NumNodes > SpillLimit ->
+ sort_on_degree(lists:seq(SpillLimit,NumNodes-1) -- Low,IG);
true -> []
end,
@@ -401,7 +401,7 @@ spill_costs([{N,Info}|Ns], IG, Vis, Spill, SpillLimit, Target) ->
true ->
spill_costs(Ns, IG, Vis, Spill, SpillLimit, Target);
false ->
- if N > SpillLimit ->
+ if N >= SpillLimit ->
spill_costs(Ns, IG, Vis, Spill, SpillLimit, Target);
true ->
[{spill_cost_of(N,Spill)/Deg,N} |
diff --git a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
index b96920cbcf..a019c46b90 100644
--- a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
@@ -1933,7 +1933,7 @@ findCheapest([Node|Nodes], IG, Cost, Cheapest, SpillLimit) ->
%% limit are extremely expensive.
getCost(Node, IG, SpillLimit) ->
- case Node > SpillLimit of
+ case Node >= SpillLimit of
true -> inf;
false ->
SpillCost = hipe_ig:node_spill_cost(Node, IG),
diff --git a/lib/hipe/sparc/hipe_sparc_assemble.erl b/lib/hipe/sparc/hipe_sparc_assemble.erl
index 08bd47c4d2..2b82f41d23 100644
--- a/lib/hipe/sparc/hipe_sparc_assemble.erl
+++ b/lib/hipe/sparc/hipe_sparc_assemble.erl
@@ -32,7 +32,7 @@ assemble(CompiledCode, Closures, Exports, Options) ->
|| {MFA, Defun} <- CompiledCode],
%%
{ConstAlign,ConstSize,ConstMap,RefsFromConsts} =
- hipe_pack_constants:pack_constants(Code, 4),
+ hipe_pack_constants:pack_constants(Code),
%%
{CodeSize,CodeBinary,AccRefs,LabelMap,ExportMap} =
encode(translate(Code, ConstMap), Options),
diff --git a/lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl b/lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl
index caa0e71d0b..430e097b91 100644
--- a/lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl
+++ b/lib/hipe/test/basic_SUITE_data/basic_bugs_hipe.erl
@@ -18,6 +18,7 @@ test() ->
ok = test_R12B5_seg_fault(),
ok = test_switch_neg_int(),
ok = test_icode_range_anal(),
+ ok = test_icode_range_call(),
ok.
%%-----------------------------------------------------------------------
@@ -461,3 +462,44 @@ g(X, Z) ->
test -> non_zero_test;
other -> other
end.
+
+%%-----------------------------------------------------------------------
+%% From: Rich Neswold
+%% Date: Oct 5, 2016
+%%
+%% The following was a bug in the HiPE compiler's range analysis. The
+%% function range_client/2 below would would not stop when N reached 0,
+%% but keep recursing into the second clause forever.
+%%
+%% The problem turned out to be in hipe_icode_range:analyse_call/2,
+%% which would note update the argument ranges of the callee if the
+%% result of the call was ignored.
+%% -----------------------------------------------------------------------
+-define(TIMEOUT, 42).
+
+test_icode_range_call() ->
+ Self = self(),
+ Client = spawn_link(fun() -> range_client(Self, 4) end),
+ range_server(4, Client).
+
+range_server(0, _Client) ->
+ receive
+ stopping -> ok;
+ {called_with, 0} -> error(failure)
+ after ?TIMEOUT -> error(timeout)
+ end;
+range_server(N, Client) ->
+ receive
+ {called_with, N} ->
+ Client ! proceed
+ after ?TIMEOUT -> error(timeout)
+ end,
+ range_server(N-1, Client). % tailcall (so the bug does not affect it)
+
+range_client(Server, 0) ->
+ Server ! stopping;
+range_client(Server, N) ->
+ Server ! {called_with, N},
+ receive proceed -> ok end,
+ range_client(Server, N - 1), % non-tailrecursive call with ignored result
+ ok.
diff --git a/lib/hipe/x86/hipe_x86_assemble.erl b/lib/hipe/x86/hipe_x86_assemble.erl
index fb0beba293..50919bdf4e 100644
--- a/lib/hipe/x86/hipe_x86_assemble.erl
+++ b/lib/hipe/x86/hipe_x86_assemble.erl
@@ -63,7 +63,7 @@ assemble(CompiledCode, Closures, Exports, Options) ->
|| {MFA, Defun} <- CompiledCode],
%%
{ConstAlign,ConstSize,ConstMap,RefsFromConsts} =
- hipe_pack_constants:pack_constants(Code, ?HIPE_X86_REGISTERS:alignment()),
+ hipe_pack_constants:pack_constants(Code),
%%
{CodeSize,CodeBinary,AccRefs,LabelMap,ExportMap} =
encode(translate(Code, ConstMap, Options), Options),
diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
index af189a74f7..b3a18e03d4 100644
--- a/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
@@ -58,7 +58,7 @@
#include "erl_interface.h"
#include "m_i.h"
-#define HOSTNAMESZ 256
+#define HOSTNAMESZ 255
#define NODENAMESZ 512
#define INBUFSZ 10
@@ -295,7 +295,7 @@ int main(int argc, char **argv)
progname = argv[0];
host[HOSTNAMESZ] = '\0';
- if (gethostname(host, HOSTNAMESZ) < 0) {
+ if (gethostname(host, HOSTNAMESZ + 1) < 0) {
fprintf(stderr, "Can't find own hostname\n");
done(1);
}
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
index b7609d63e5..40c7328f03 100644
--- a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
@@ -61,7 +61,7 @@
#include "erl_interface.h"
#include "m_i.h"
-#define HOSTNAMESZ 256
+#define HOSTNAMESZ 255
#define NODENAMESZ 512
#define INBUFSZ 10
@@ -298,7 +298,7 @@ int main(int argc, char **argv)
progname = argv[0];
host[HOSTNAMESZ] = '\0';
- if (gethostname(host, HOSTNAMESZ) < 0) {
+ if (gethostname(host, HOSTNAMESZ + 1) < 0) {
fprintf(stderr, "Can't find own hostname\n");
done(1);
}
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
index 23dc089555..33cfe71322 100644
--- a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
@@ -61,7 +61,7 @@
#include "erl_interface.h"
#include "m_i.h"
-#define HOSTNAMESZ 256
+#define HOSTNAMESZ 255
#define NODENAMESZ 512
#define INBUFSZ 10
@@ -298,7 +298,7 @@ int main(int argc, char **argv)
progname = argv[0];
host[HOSTNAMESZ] = '\0';
- if (gethostname(host, HOSTNAMESZ) < 0) {
+ if (gethostname(host, HOSTNAMESZ + 1) < 0) {
fprintf(stderr, "Can't find own hostname\n");
done(1);
}
diff --git a/lib/ic/test/erl_client_c_server_SUITE_data/c_server.c b/lib/ic/test/erl_client_c_server_SUITE_data/c_server.c
index 53345d561b..f48480e8dc 100644
--- a/lib/ic/test/erl_client_c_server_SUITE_data/c_server.c
+++ b/lib/ic/test/erl_client_c_server_SUITE_data/c_server.c
@@ -81,7 +81,7 @@ static void showtime(MyTimeval *start, MyTimeval *stop);
static void usage(void);
static void done(int r);
-#define HOSTNAMESZ 256
+#define HOSTNAMESZ 255
#define NODENAMESZ 512
#define INBUFSZ 10
#define OUTBUFSZ 0
@@ -122,7 +122,7 @@ int main(int argc, char **argv)
progname = argv[0];
host[HOSTNAMESZ] = '\0';
- if (gethostname(host, HOSTNAMESZ) < 0) {
+ if (gethostname(host, HOSTNAMESZ + 1) < 0) {
fprintf(stderr, "Can't find own hostname\n");
done(1);
}
diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/c_server.c b/lib/ic/test/erl_client_c_server_proto_SUITE_data/c_server.c
index a18f0e7ba9..e2ba5bd5b6 100644
--- a/lib/ic/test/erl_client_c_server_proto_SUITE_data/c_server.c
+++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/c_server.c
@@ -81,7 +81,7 @@ static void showtime(MyTimeval *start, MyTimeval *stop);
static void usage(void);
static void done(int r);
-#define HOSTNAMESZ 256
+#define HOSTNAMESZ 255
#define NODENAMESZ 512
#define INBUFSZ 10
#define OUTBUFSZ 0
@@ -122,7 +122,7 @@ int main(int argc, char **argv)
progname = argv[0];
host[HOSTNAMESZ] = '\0';
- if (gethostname(host, HOSTNAMESZ) < 0) {
+ if (gethostname(host, HOSTNAMESZ + 1) < 0) {
fprintf(stderr, "Can't find own hostname\n");
done(1);
}
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index e97db20062..bef8096aed 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -140,6 +140,23 @@ do_recv(Sock, Bs) ->
<fsummary>Close a TCP socket.</fsummary>
<desc>
<p>Closes a TCP socket.</p>
+ <p>Note that in most implementations of TCP, doing a <c>close</c> does
+ not guarantee that any data sent is delivered to the recipient before
+ the close is detected at the remote side. If you want to guarantee
+ delivery of the data to the recipient there are two common ways to
+ achieve this.</p>
+ <list type="ordered">
+ <item><p>Use <seealso marker="#shutdown/2">
+ <c>gen_tcp:shutdown(Sock, write)</c></seealso> to signal that
+ no more data is to be sent and wait for the read side of the
+ socket to be closed.</p>
+ </item>
+ <item><p>Use the socket option <seealso marker="inet#packet">
+ <c>{packet, N}</c></seealso> (or something similar) to make
+ it possible for the receiver to close the connection when it
+ knowns it has received all the data.</p>
+ </item>
+ </list>
</desc>
</func>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index d557efb6a8..076e50cd10 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -913,7 +913,7 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp</code>
</item>
<tag><c>{packet, PacketType}</c>(TCP/IP sockets)</tag>
<item>
- <p>Defines the type of packets to use for a socket.
+ <p><marker id="packet"/>Defines the type of packets to use for a socket.
Possible values:</p>
<taglist>
<tag><c>raw | 0</c></tag>
diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl
index 59eca242b1..b901da95b8 100644
--- a/lib/kernel/src/kernel.erl
+++ b/lib/kernel/src/kernel.erl
@@ -100,63 +100,112 @@ get_error_logger_type() ->
%%%-----------------------------------------------------------------
init([]) ->
- SupFlags = {one_for_all, 0, 1},
-
- Config = {kernel_config,
- {kernel_config, start_link, []},
- permanent, 2000, worker, [kernel_config]},
- Code = {code_server,
- {code, start_link, []},
- permanent, 2000, worker, [code]},
- File = {file_server_2,
- {file_server, start_link, []},
- permanent, 2000, worker,
- [file, file_server, file_io_server, prim_file]},
- StdError = {standard_error,
- {standard_error, start_link, []},
- temporary, 2000, supervisor, [user_sup]},
- User = {user,
- {user_sup, start, []},
- temporary, 2000, supervisor, [user_sup]},
-
+ SupFlags = #{strategy => one_for_all,
+ intensity => 0,
+ period => 1},
+
+ Config = #{id => kernel_config,
+ start => {kernel_config, start_link, []},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [kernel_config]},
+
+ Code = #{id => code_server,
+ start => {code, start_link, []},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [code]},
+
+ File = #{id => file_server_2,
+ start => {file_server, start_link, []},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modeules => [file, file_server, file_io_server, prim_file]},
+
+ StdError = #{id => standard_error,
+ start => {standard_error, start_link, []},
+ restart => temporary,
+ shutdown => 2000,
+ type => supervisor,
+ modules => [user_sup]},
+
+ User = #{id => user,
+ start => {user_sup, start, []},
+ restart => temporary,
+ shutdown => 2000,
+ type => supervisor,
+ modules => [user_sup]},
+
+ SafeSup = #{id => kernel_safe_sup,
+ start =>{supervisor, start_link, [{local, kernel_safe_sup}, ?MODULE, safe]},
+ restart => permanent,
+ shutdown => infinity,
+ type => supervisor,
+ modules => [?MODULE]},
+
case init:get_argument(mode) of
- {ok, [["minimal"]]} ->
- SafeSupervisor = {kernel_safe_sup,
- {supervisor, start_link,
- [{local, kernel_safe_sup}, ?MODULE, safe]},
- permanent, infinity, supervisor, [?MODULE]},
- {ok, {SupFlags,
- [Code, File, StdError, User,
- Config, SafeSupervisor]}};
- _ ->
- Rpc = {rex, {rpc, start_link, []},
- permanent, 2000, worker, [rpc]},
- Global = {global_name_server, {global, start_link, []},
- permanent, 2000, worker, [global]},
- Glo_grp = {global_group, {global_group,start_link,[]},
- permanent, 2000, worker, [global_group]},
- InetDb = {inet_db, {inet_db, start_link, []},
- permanent, 2000, worker, [inet_db]},
- NetSup = {net_sup, {erl_distribution, start_link, []},
- permanent, infinity, supervisor,[erl_distribution]},
+ {ok, [["minimal"]]} ->
+ {ok, {SupFlags, [Code, File, StdError, User, Config, SafeSup]}};
+ _ ->
+ Rpc = #{id => rex,
+ start => {rpc, start_link, []},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [rpc]},
+
+ Global = #{id => global_name_server,
+ start => {global, start_link, []},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [global]},
+
+ GlGroup = #{id => global_group,
+ start => {global_group,start_link,[]},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [global_group]},
+
+ InetDb = #{id => inet_db,
+ start => {inet_db, start_link, []},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [inet_db]},
+
+ NetSup = #{id => net_sup,
+ start => {erl_distribution, start_link, []},
+ restart => permanent,
+ shutdown => infinity,
+ type => supervisor,
+ modules => [erl_distribution]},
+
SigSrv = #{id => erl_signal_server,
start => {gen_event, start_link, [{local, erl_signal_server}]},
- type => worker, restart => permanent, shutdown => 2000, modules => dynamic},
- DistAC = start_dist_ac(),
-
- Timer = start_timer(),
-
- SafeSupervisor = {kernel_safe_sup,
- {supervisor, start_link,
- [{local, kernel_safe_sup}, ?MODULE, safe]},
- permanent, infinity, supervisor, [?MODULE]},
- {ok, {SupFlags,
- [Code, Rpc, Global, InetDb | DistAC] ++
- [NetSup, Glo_grp, File, SigSrv,
- StdError, User, Config, SafeSupervisor] ++ Timer}}
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => dynamic},
+
+ DistAC = start_dist_ac(),
+
+ Timer = start_timer(),
+
+ {ok, {SupFlags,
+ [Code, Rpc, Global, InetDb | DistAC] ++
+ [NetSup, GlGroup, File, SigSrv,
+ StdError, User, Config, SafeSup] ++ Timer}}
end;
init(safe) ->
- SupFlags = {one_for_one, 4, 3600},
+ SupFlags = #{strategy => one_for_one,
+ intensity => 4,
+ period => 3600},
+
Boot = start_boot_server(),
DiskLog = start_disk_log(),
Pg2 = start_pg2(),
@@ -170,60 +219,85 @@ init(safe) ->
{ok, {SupFlags, Boot ++ DiskLog ++ Pg2}}.
start_dist_ac() ->
- Spec = [{dist_ac,{dist_ac,start_link,[]},permanent,2000,worker,[dist_ac]}],
+ Spec = [#{id => dist_ac,
+ start => {dist_ac,start_link,[]},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [dist_ac]}],
case application:get_env(kernel, start_dist_ac) of
- {ok, true} -> Spec;
- {ok, false} -> [];
- undefined ->
- case application:get_env(kernel, distributed) of
- {ok, _} -> Spec;
- _ -> []
- end
+ {ok, true} -> Spec;
+ {ok, false} -> [];
+ undefined ->
+ case application:get_env(kernel, distributed) of
+ {ok, _} -> Spec;
+ _ -> []
+ end
end.
start_boot_server() ->
case application:get_env(kernel, start_boot_server) of
- {ok, true} ->
- Args = get_boot_args(),
- [{boot_server, {erl_boot_server, start_link, [Args]}, permanent,
- 1000, worker, [erl_boot_server]}];
- _ ->
- []
+ {ok, true} ->
+ Args = get_boot_args(),
+ [#{id => boot_server,
+ start => {erl_boot_server, start_link, [Args]},
+ restart => permanent,
+ shutdown => 1000,
+ type => worker,
+ modules => [erl_boot_server]}];
+ _ ->
+ []
end.
get_boot_args() ->
case application:get_env(kernel, boot_server_slaves) of
- {ok, Slaves} -> Slaves;
- _ -> []
+ {ok, Slaves} -> Slaves;
+ _ -> []
end.
start_disk_log() ->
case application:get_env(kernel, start_disk_log) of
- {ok, true} ->
- [{disk_log_server,
- {disk_log_server, start_link, []},
- permanent, 2000, worker, [disk_log_server]},
- {disk_log_sup, {disk_log_sup, start_link, []}, permanent,
- 1000, supervisor, [disk_log_sup]}];
- _ ->
- []
+ {ok, true} ->
+ [#{id => disk_log_server,
+ start => {disk_log_server, start_link, []},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [disk_log_server]},
+ #{id => disk_log_sup,
+ start => {disk_log_sup, start_link, []},
+ restart => permanent,
+ shutdown => 1000,
+ type => supervisor,
+ modules => [disk_log_sup]}];
+ _ ->
+ []
end.
start_pg2() ->
case application:get_env(kernel, start_pg2) of
- {ok, true} ->
- [{pg2, {pg2, start_link, []}, permanent, 1000, worker, [pg2]}];
- _ ->
- []
+ {ok, true} ->
+ [#{id => pg2,
+ start => {pg2, start_link, []},
+ restart => permanent,
+ shutdown => 1000,
+ type => worker,
+ modules => [pg2]}];
+ _ ->
+ []
end.
start_timer() ->
case application:get_env(kernel, start_timer) of
- {ok, true} ->
- [{timer_server, {timer, start_link, []}, permanent, 1000, worker,
- [timer]}];
- _ ->
- []
+ {ok, true} ->
+ [#{id => timer_server,
+ start => {timer, start_link, []},
+ restart => permanent,
+ shutdown => 1000,
+ type => worker,
+ modules => [timer]}];
+ _ ->
+ []
end.
%%-----------------------------------------------------------------
diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl
index cad02087be..9e1442a5ca 100644
--- a/lib/observer/src/observer_alloc_wx.erl
+++ b/lib/observer/src/observer_alloc_wx.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
-module(observer_alloc_wx).
--export([start_link/2]).
+-export([start_link/3]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
@@ -49,10 +49,10 @@
[make_win/4, setup_graph_drawing/1, refresh_panel/4, interval_dialog/2,
add_data/5, precalc/4]).
-start_link(Notebook, Parent) ->
- wx_object:start_link(?MODULE, [Notebook, Parent], []).
+start_link(Notebook, Parent, Config) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent, Config], []).
-init([Notebook, Parent]) ->
+init([Notebook, Parent, Config]) ->
try
TopP = wxPanel:new(Notebook),
Main = wxBoxSizer:new(?wxVERTICAL),
@@ -75,7 +75,7 @@ init([Notebook, Parent]) ->
wins = Windows,
mem = MemWin,
paint = PaintInfo,
- time = setup_time(),
+ time = setup_time(Config),
max = #{}
}
}
@@ -84,9 +84,11 @@ init([Notebook, Parent]) ->
{stop, Err}
end.
-setup_time() ->
- Freq = 1,
- #ti{fetch=Freq, disp=?DISP_FREQ/Freq}.
+setup_time(Config) ->
+ Freq = maps:get(fetch, Config, 1),
+ #ti{disp=?DISP_FREQ/Freq,
+ fetch=Freq,
+ secs=maps:get(secs, Config, ?DISP_SECONDS)}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
handle_event(#wx{id=?ID_REFRESH_INTERVAL, event=#wxCommand{type=command_menu_selected}},
@@ -117,6 +119,10 @@ handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_,
refresh_panel(Active, Win, Ti, Paint),
ok.
%%%%%%%%%%
+handle_call(get_config, _, #state{time=Ti}=State) ->
+ #ti{fetch=Fetch, secs=Range} = Ti,
+ {reply, #{fetch=>Fetch, secs=>Range}, State};
+
handle_call(Event, From, _State) ->
error({unhandled_call, Event, From}).
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index 80a41fdde9..63ca3aeba7 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
-module(observer_app_wx).
--export([start_link/2]).
+-export([start_link/3]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
@@ -73,10 +73,10 @@
-define(wxGC, wxGraphicsContext).
-start_link(Notebook, Parent) ->
- wx_object:start_link(?MODULE, [Notebook, Parent], []).
+start_link(Notebook, Parent, Config) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent, Config], []).
-init([Notebook, Parent]) ->
+init([Notebook, Parent, _Config]) ->
Panel = wxPanel:new(Notebook, [{size, wxWindow:getClientSize(Notebook)},
{winid, 1}
]),
@@ -258,6 +258,8 @@ handle_sync_event(#wx{event = #wxPaint{}},_,
destroy_gc(GC),
ok.
%%%%%%%%%%
+handle_call(get_config, _, State) ->
+ {reply, #{}, State};
handle_call(Event, From, _State) ->
error({unhandled_call, Event, From}).
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 47844c1307..68095d7f58 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -24,7 +24,7 @@
display_progress_dialog/2, destroy_progress_dialog/0,
wait_for_progress/0, report_progress/1,
user_term/3, user_term_multiline/3,
- interval_dialog/4, start_timer/1, stop_timer/1,
+ interval_dialog/4, start_timer/1, start_timer/2, stop_timer/1, timer_config/1,
display_info/2, display_info/3, fill_info/2, update_info/2, to_str/1,
create_menus/3, create_menu_item/3,
create_attrs/0,
@@ -90,6 +90,12 @@ stop_timer(Timer = {true, _}) -> Timer;
stop_timer(Timer = {_, Intv}) ->
setup_timer(false, Timer),
{true, Intv}.
+
+start_timer(#{interval:=Intv}, _Def) ->
+ setup_timer(true, {false, Intv});
+start_timer(_, Def) ->
+ setup_timer(true, {false, Def}).
+
start_timer(Intv) when is_integer(Intv) ->
setup_timer(true, {true, Intv});
start_timer(Timer) ->
@@ -105,6 +111,11 @@ setup_timer(Bool, {Timer, Old}) ->
timer:cancel(Timer),
setup_timer(Bool, {false, Old}).
+timer_config({_, Interval}) ->
+ #{interval=>Interval};
+timer_config(#{}=Config) ->
+ Config.
+
display_info_dialog(Parent,Str) ->
display_info_dialog(Parent,"",Str).
display_info_dialog(Parent,Title,Str) ->
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index 0cbcdbceb4..fc5fb226db 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
-module(observer_perf_wx).
--export([start_link/2]).
+-export([start_link/3]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
@@ -57,10 +57,10 @@
-record(paint, {font, small, pen, pen2, pens, dot_pens, usegc = false}).
-start_link(Notebook, Parent) ->
- wx_object:start_link(?MODULE, [Notebook, Parent], []).
+start_link(Notebook, Parent, Config) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent, Config], []).
-init([Notebook, Parent]) ->
+init([Notebook, Parent, Config]) ->
try
Panel = wxPanel:new(Notebook),
Main = wxBoxSizer:new(?wxVERTICAL),
@@ -81,7 +81,9 @@ init([Notebook, Parent]) ->
panel =Panel,
wins = Windows,
paint=PaintInfo,
- samples=reset_data()
+ samples=reset_data(),
+ time=#ti{fetch=maps:get(fetch, Config, ?FETCH_DATA),
+ secs=maps:get(secs, Config, ?DISP_SECONDS)}
},
{Panel, State0}
catch _:Err ->
@@ -177,6 +179,10 @@ refresh_panel(Active, #win{name=_Id, panel=Panel}=Win, Ti, #paint{usegc=UseGC}=P
destroy_gc(GC).
%%%%%%%%%%
+handle_call(get_config, _, #state{time=Ti}=State) ->
+ #ti{fetch=Fetch, secs=Range} = Ti,
+ {reply, #{fetch=>Fetch, secs=>Range}, State};
+
handle_call(Event, From, _State) ->
error({unhandled_call, Event, From}).
@@ -210,7 +216,7 @@ handle_info({refresh, Seq}, #state{panel=Panel, time=#ti{tick=Seq, disp=DispF}=T
handle_info({refresh, _}, State) ->
{noreply, State};
-handle_info({active, Node}, #state{parent=Parent, panel=Panel, appmon=Old, time=_Ti} = State) ->
+handle_info({active, Node}, #state{parent=Parent, panel=Panel, appmon=Old} = State) ->
create_menus(Parent, []),
try
Node = node(Old),
diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl
index c21d2705c0..db5e6ceb38 100644
--- a/lib/observer/src/observer_port_wx.erl
+++ b/lib/observer/src/observer_port_wx.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
-module(observer_port_wx).
--export([start_link/2]).
+-export([start_link/3]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
@@ -77,10 +77,10 @@
open_wins=[]
}).
-start_link(Notebook, Parent) ->
- wx_object:start_link(?MODULE, [Notebook, Parent], []).
+start_link(Notebook, Parent, Config) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent, Config], []).
-init([Notebook, Parent]) ->
+init([Notebook, Parent, Config]) ->
Panel = wxPanel:new(Notebook),
Sizer = wxBoxSizer:new(?wxVERTICAL),
Style = ?wxLC_REPORT bor ?wxLC_HRULES,
@@ -110,12 +110,12 @@ init([Notebook, Parent]) ->
wxListCtrl:connect(Grid, size, [{skip, true}]),
wxWindow:setFocus(Grid),
- {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}.
+ {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer=Config}}.
handle_event(#wx{id=?ID_REFRESH},
State = #state{node=Node, grid=Grid, opt=Opt}) ->
Ports0 = get_ports(Node),
- Ports = update_grid(Grid, Opt, Ports0),
+ Ports = update_grid(Grid, sel(State), Opt, Ports0),
{noreply, State#state{ports=Ports}};
handle_event(#wx{obj=Obj, event=#wxClose{}}, #state{open_wins=Opened} = State) ->
@@ -134,7 +134,7 @@ handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
NewKey -> Opt0#opt{sort_key=NewKey}
end,
Ports0 = get_ports(Node),
- Ports = update_grid(Grid, Opt, Ports0),
+ Ports = update_grid(Grid, sel(State), Opt, Ports0),
wxWindow:setFocus(Grid),
{noreply, State#state{opt=Opt, ports=Ports}};
@@ -260,6 +260,9 @@ handle_event(Event, _State) ->
handle_sync_event(_Event, _Obj, _State) ->
ok.
+handle_call(get_config, _, #state{timer=Timer}=State) ->
+ {reply, observer_lib:timer_config(Timer), State};
+
handle_call(Event, From, _State) ->
error({unhandled_call, Event, From}).
@@ -269,7 +272,7 @@ handle_cast(Event, _State) ->
handle_info({portinfo_open, PortIdStr},
State = #state{node=Node, grid=Grid, opt=Opt, open_wins=Opened}) ->
Ports0 = get_ports(Node),
- Ports = update_grid(Grid, Opt, Ports0),
+ Ports = update_grid(Grid, sel(State), Opt, Ports0),
Port = lists:keyfind(PortIdStr, #port.id_str, Ports),
NewOpened =
case Port of
@@ -288,17 +291,17 @@ handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
%% no change
{noreply, State};
Ports0 ->
- Ports = update_grid(Grid, Opt, Ports0),
+ Ports = update_grid(Grid, sel(State), Opt, Ports0),
{noreply, State#state{ports=Ports}}
end;
handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt,
timer=Timer0}) ->
Ports0 = get_ports(Node),
- Ports = update_grid(Grid, Opt, Ports0),
+ Ports = update_grid(Grid, sel(State), Opt, Ports0),
wxWindow:setFocus(Grid),
create_menus(Parent),
- Timer = observer_lib:start_timer(Timer0),
+ Timer = observer_lib:start_timer(Timer0, 10),
{noreply, State#state{node=Node, ports=Ports, timer=Timer}};
handle_info(not_active, State = #state{timer = Timer0}) ->
@@ -511,9 +514,9 @@ filter_monitor_info() ->
[Pid || {process, Pid} <- Ms]
end.
-update_grid(Grid, Opt, Ports) ->
- wx:batch(fun() -> update_grid2(Grid, Opt, Ports) end).
-update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) ->
+update_grid(Grid, Sel, Opt, Ports) ->
+ wx:batch(fun() -> update_grid2(Grid, Sel, Opt, Ports) end).
+update_grid2(Grid, Sel, #opt{sort_key=Sort,sort_incr=Dir}, Ports) ->
wxListCtrl:deleteAllItems(Grid),
Update =
fun(#port{id = Id,
@@ -533,6 +536,12 @@ update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) ->
observer_lib:to_str(Val))
end,
[{0,Id},{1,Connected},{2,Name},{3,Ctrl},{4,Slot}]),
+ case lists:member(Id, Sel) of
+ true ->
+ wxListCtrl:setItemState(Grid, Row, 16#FFFF, ?wxLIST_STATE_SELECTED);
+ false ->
+ wxListCtrl:setItemState(Grid, Row, 0, ?wxLIST_STATE_SELECTED)
+ end,
Row + 1
end,
PortInfo = case Dir of
@@ -542,6 +551,8 @@ update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) ->
lists:foldl(Update, 0, PortInfo),
PortInfo.
+sel(#state{grid=Grid, ports=Ports}) ->
+ [Id || #port{id=Id} <- get_selected_items(Grid, Ports)].
get_selected_items(Grid, Data) ->
get_indecies(get_selected_items(Grid, -1, []), Data).
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index f07b9e295a..3ecf8bdd92 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -20,7 +20,7 @@
-behaviour(wx_object).
--export([start_link/2]).
+-export([start_link/3]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
@@ -86,18 +86,19 @@
right_clicked_pid,
holder}).
-start_link(Notebook, Parent) ->
- wx_object:start_link(?MODULE, [Notebook, Parent], []).
+start_link(Notebook, Parent, Config) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent, Config], []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init([Notebook, Parent]) ->
+init([Notebook, Parent, Config]) ->
Attrs = observer_lib:create_attrs(),
Self = self(),
- Holder = spawn_link(fun() -> init_table_holder(Self, Attrs) end),
- {ProPanel, State} = setup(Notebook, Parent, Holder),
+ Acc = maps:get(acc, Config, false),
+ Holder = spawn_link(fun() -> init_table_holder(Self, Acc, Attrs) end),
+ {ProPanel, State} = setup(Notebook, Parent, Holder, Config),
{ProPanel, State#state{holder=Holder}}.
-setup(Notebook, Parent, Holder) ->
+setup(Notebook, Parent, Holder, Config) ->
ProPanel = wxPanel:new(Notebook, []),
Grid = create_list_box(ProPanel, Holder),
@@ -113,7 +114,7 @@ setup(Notebook, Parent, Holder) ->
panel=ProPanel,
parent_notebook=Notebook,
holder=Holder,
- timer={false, 10}
+ timer=Config
},
{ProPanel, State}.
@@ -246,7 +247,7 @@ handle_info({active, Node},
#state{holder=Holder, timer=Timer, parent=Parent}=State) ->
create_pro_menu(Parent, Holder),
Holder ! {change_node, Node},
- {noreply, State#state{timer=observer_lib:start_timer(Timer)}};
+ {noreply, State#state{timer=observer_lib:start_timer(Timer, 10)}};
handle_info(not_active, #state{timer=Timer0}=State) ->
Timer = observer_lib:stop_timer(Timer0),
@@ -264,11 +265,15 @@ terminate(_Reason, #state{holder=Holder}) ->
code_change(_, _, State) ->
{ok, State}.
+handle_call(get_config, _, #state{holder=Holder, timer=Timer}=State) ->
+ Conf = observer_lib:timer_config(Timer),
+ Accum = call(Holder, {get_accum, self()}),
+ {reply, Conf#{acc=>Accum}, State};
+
handle_call(Msg, _From, State) ->
io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]),
{reply, ok, State}.
-
handle_cast(Msg, State) ->
io:format("~p:~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]),
{noreply, State}.
@@ -453,14 +458,19 @@ rm_selected(_, [], [], AccIds, AccPids) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init_table_holder(Parent, Attrs) ->
+init_table_holder(Parent, Accum0, Attrs) ->
Backend = spawn_link(node(), observer_backend,etop_collect,[self()]),
+ Accum = case Accum0 of
+ true -> true;
+ false -> []
+ end,
table_holder(#holder{parent=Parent,
etop=#etop_info{},
info=array:new(),
node=node(),
backend_pid=Backend,
- attrs=Attrs
+ attrs=Attrs,
+ accum=Accum
}).
table_holder(#holder{info=Info, attrs=Attrs,
diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl
index fa824995f7..2529e79e20 100644
--- a/lib/observer/src/observer_sys_wx.erl
+++ b/lib/observer/src/observer_sys_wx.erl
@@ -20,7 +20,7 @@
-behaviour(wx_object).
--export([start_link/2]).
+-export([start_link/3]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_event/2, handle_cast/2]).
@@ -41,12 +41,12 @@
fields,
timer}).
-start_link(Notebook, Parent) ->
- wx_object:start_link(?MODULE, [Notebook, Parent], []).
+start_link(Notebook, Parent, Config) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent, Config], []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init([Notebook, Parent]) ->
+init([Notebook, Parent, Config]) ->
SysInfo = observer_backend:sys_info(),
{Sys, Mem, Cpu, Stats} = info_fields(),
Panel = wxPanel:new(Notebook),
@@ -69,7 +69,7 @@ init([Notebook, Parent]) ->
wxSizer:add(Sizer, HSizer1, [{flag, ?wxEXPAND bor BorderFlags bor ?wxBOTTOM},
{proportion, 0}, {border, 5}]),
wxPanel:setSizer(Panel, Sizer),
- Timer = observer_lib:start_timer(10),
+ Timer = observer_lib:start_timer(Config, 10),
{Panel, #sys_wx_state{parent=Parent,
parent_notebook=Notebook,
panel=Panel, sizer=Sizer,
@@ -167,6 +167,9 @@ terminate(_Reason, _State) ->
code_change(_, _, State) ->
{ok, State}.
+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]),
{reply, ok, State}.
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index af90e2100c..247a4608d5 100644
--- a/lib/observer/src/observer_trace_wx.erl
+++ b/lib/observer/src/observer_trace_wx.erl
@@ -19,7 +19,7 @@
-module(observer_trace_wx).
--export([start_link/2, add_processes/1, add_ports/1]).
+-export([start_link/3, add_processes/1, add_ports/1]).
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_event/2, handle_cast/2]).
@@ -88,8 +88,8 @@
-record(titem, {id, opts}).
-start_link(Notebook, ParentPid) ->
- wx_object:start_link(?MODULE, [Notebook, ParentPid], []).
+start_link(Notebook, ParentPid, Config) ->
+ wx_object:start_link(?MODULE, [Notebook, ParentPid, Config], []).
add_processes(Pids) when is_list(Pids) ->
wx_object:cast(observer_wx:get_tracer(), {add_processes, Pids}).
@@ -99,10 +99,10 @@ add_ports(Ports) when is_list(Ports) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init([Notebook, ParentPid]) ->
- wx:batch(fun() -> create_window(Notebook, ParentPid) end).
+init([Notebook, ParentPid, Config]) ->
+ wx:batch(fun() -> create_window(Notebook, ParentPid, Config) end).
-create_window(Notebook, ParentPid) ->
+create_window(Notebook, ParentPid, Config) ->
%% Create the window
Panel = wxPanel:new(Notebook, [{size, wxWindow:getClientSize(Notebook)}]),
Sizer = wxBoxSizer:new(?wxVERTICAL),
@@ -130,11 +130,16 @@ create_window(Notebook, ParentPid) ->
wxSizer:add(Sizer, Buttons, [{flag, ?wxLEFT bor ?wxRIGHT bor ?wxDOWN},
{border, 5}, {proportion,0}]),
wxWindow:setSizer(Panel, Sizer),
+ MS = parse_ms(maps:get(match_specs, Config, []), default_matchspecs()),
{Panel, #state{parent=ParentPid, panel=Panel,
n_view=NodeView, proc_view=ProcessView, port_view=PortView,
m_view=ModView, f_view=FuncView,
toggle_button = ToggleButton,
- match_specs=default_matchspecs()}}.
+ output=maps:get(output, Config, []),
+ def_proc_flags=maps:get(procflags, Config, []),
+ def_port_flags=maps:get(portflags, Config, []),
+ match_specs=MS
+ }}.
default_matchspecs() ->
[{Key,default_matchspecs(Key)} || Key <- [funcs,send,'receive']].
@@ -397,27 +402,19 @@ handle_event(#wx{id=?LOG_SAVE, userData=TCtrl}, #state{panel=Panel} = State) ->
{noreply, State};
handle_event(#wx{id = ?SAVE_TRACEOPTS},
- #state{panel = Panel,
- def_proc_flags = ProcFlags,
- def_port_flags = PortFlags,
- match_specs = MatchSpecs,
- tpatterns = TracePatterns,
- output = Output
- } = State) ->
+ #state{panel = Panel} = State) ->
Dialog = wxFileDialog:new(Panel, [{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]),
case wxFileDialog:showModal(Dialog) of
?wxID_OK ->
Path = wxFileDialog:getPath(Dialog),
- write_file(Panel, Path,
- ProcFlags, PortFlags, MatchSpecs, Output,
- dict:to_list(TracePatterns)
- );
+ write_file(Panel, Path, get_config(State));
_ ->
ok
end,
wxDialog:destroy(Dialog),
{noreply, State};
+
handle_event(#wx{id = ?LOAD_TRACEOPTS}, #state{panel = Panel} = State) ->
Dialog = wxFileDialog:new(Panel, [{style, ?wxFD_FILE_MUST_EXIST}]),
State2 = case wxFileDialog:showModal(Dialog) of
@@ -690,6 +687,10 @@ handle_event(#wx{id=ID, event = What}, State) ->
{noreply, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+handle_call(get_config, _, State) ->
+ Config0 = get_config(State),
+ Config = lists:keydelete(trace_p, 1, Config0),
+ {reply, maps:from_list(Config), State};
handle_call(Msg, From, _State) ->
error({unhandled_call, Msg, From}).
@@ -1101,26 +1102,38 @@ ftup(Trace, Index, Size) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-write_file(Frame, Filename, ProcFlags, PortFlags, MatchSpecs, Output, TPs) ->
+get_config(#state{def_proc_flags = ProcFlags,
+ def_port_flags = PortFlags,
+ match_specs = MatchSpecs0,
+ tpatterns = TracePatterns,
+ output = Output}) ->
MSToList = fun(#match_spec{name=Id, term=T, func=F}) ->
[{name,Id},{term,T},{func,F}]
end,
- MSTermList = [{ms,Key,[MSToList(MS) || MS <- MSs]} ||
- {Key,MSs} <- MatchSpecs],
+ MatchSpecs = [{ms,Key,[MSToList(MS) || MS <- MSs]} ||
+ {Key,MSs} <- MatchSpecs0],
TPToTuple = fun(#tpattern{fa={F,A}, ms=Ms}) ->
- {F,A,MSToList(Ms)}
+ {F,A,MSToList(Ms)}
end,
ModuleTermList = [{tp, Module, [TPToTuple(FTP) || FTP <- FTPs]} ||
- {Module,FTPs} <- TPs],
-
+ {Module,FTPs} <- dict:to_list(TracePatterns)],
+ [{procflags,ProcFlags},
+ {portflags,PortFlags},
+ {match_specs,MatchSpecs},
+ {output,Output},
+ {trace_p,ModuleTermList}].
+
+write_file(Frame, Filename, Config) ->
Str =
["%%%\n%%% This file is generated by Observer\n",
"%%%\n%%% DO NOT EDIT!\n%%%\n",
- [io_lib:format("~p.~n",[MSTerm]) || MSTerm <- MSTermList],
- io_lib:format("~p.~n",[{procflags,ProcFlags}]),
- io_lib:format("~p.~n",[{portflags,PortFlags}]),
- io_lib:format("~p.~n",[{output,Output}]),
- [io_lib:format("~p.~n",[ModuleTerm]) || ModuleTerm <- ModuleTermList]
+ [io_lib:format("~p.~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]) ||
+ ModuleTerm <- proplists:get_value(trace_p, Config)]
],
case file:write_file(Filename, list_to_binary(Str)) of
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index 75e6919642..46da65e005 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -233,9 +233,22 @@ handle_event(#wx{id=?ID_REFRESH},State = #state{pid=Pid}) ->
{noreply, State};
handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
- State = #state{pid=Pid}) ->
+ State = #state{pid=Pid, grid=Grid, selected=OldSel}) ->
+ SelObj = case OldSel of
+ undefined -> undefined;
+ _ -> get_row(Pid, OldSel, term)
+ end,
Pid ! {sort, Col+1},
- {noreply, State};
+ case SelObj =/= undefined andalso search(Pid, SelObj, -1, true, term) of
+ false when is_integer(OldSel) ->
+ wxListCtrl:setItemState(Grid, OldSel, 0, ?wxLIST_STATE_SELECTED),
+ {noreply, State#state{selected=undefined}};
+ false ->
+ {noreply, State#state{selected=undefined}};
+ Row ->
+ wxListCtrl:setItemState(Grid, Row, 16#FFFF, ?wxLIST_STATE_SELECTED),
+ {noreply, State#state{selected=Row}}
+ end;
handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
observer_lib:set_listctrl_col_size(Grid, W),
@@ -607,6 +620,17 @@ keysort(Col, Table) ->
end,
lists:sort(Sort, Table).
+search([Term, -1, true, term], S=#holder{parent=Parent, table=Table}) ->
+ Search = fun(Idx, [Tuple|_]) ->
+ Tuple =:= Term andalso throw(Idx),
+ Tuple
+ end,
+ try array:map(Search, Table) of
+ _ -> Parent ! {self(), false}
+ catch Index ->
+ Parent ! {self(), Index}
+ end,
+ S;
search([Str, Row, Dir0, CaseSens],
S=#holder{parent=Parent, n=N, table=Table}) ->
Opt = case CaseSens of
@@ -642,6 +666,8 @@ get_row(From, Row, Col, Table) ->
From ! {self(), format(Object)};
[Object|_] when Col =:= all_multiline ->
From ! {self(), io_lib:format("~p", [Object])};
+ [Object|_] when Col =:= term ->
+ From ! {self(), Object};
[Object|_] when tuple_size(Object) >= Col ->
From ! {self(), format(element(Col, Object))};
_ ->
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index d04fb839c8..e112c54534 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -18,7 +18,7 @@
%% %CopyrightEnd%
-module(observer_tv_wx).
--export([start_link/2, display_table_info/4]).
+-export([start_link/3, display_table_info/4]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
@@ -58,10 +58,10 @@
timer
}).
-start_link(Notebook, Parent) ->
- wx_object:start_link(?MODULE, [Notebook, Parent], []).
+start_link(Notebook, Parent, Config) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent, Config], []).
-init([Notebook, Parent]) ->
+init([Notebook, Parent, Config]) ->
Panel = wxPanel:new(Notebook),
Sizer = wxBoxSizer:new(?wxVERTICAL),
Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES,
@@ -94,25 +94,31 @@ init([Notebook, Parent]) ->
wxListCtrl:connect(Grid, size, [{skip, true}]),
wxWindow:setFocus(Grid),
- {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}.
+ {Panel, #state{grid=Grid, parent=Parent, panel=Panel,
+ timer=Config,
+ opt=#opt{type=maps:get(type, Config, ets),
+ sys_hidden=maps:get(sys_hidden, Config, true),
+ unread_hidden=maps:get(unread_hidden, Config, true)}
+ }}.
handle_event(#wx{id=?ID_REFRESH},
State = #state{node=Node, grid=Grid, opt=Opt}) ->
Tables = get_tables(Node, Opt),
- Tabs = update_grid(Grid, Opt, Tables),
- {noreply, State#state{tabs=Tabs}};
+ {Tabs,Sel} = update_grid(Grid, sel(State), Opt, Tables),
+ Sel =/= undefined andalso wxListCtrl:ensureVisible(Grid, Sel),
+ {noreply, State#state{tabs=Tabs, selected=Sel}};
handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
State = #state{node=Node, grid=Grid,
opt=Opt0=#opt{sort_key=Key, sort_incr=Bool}}) ->
- Opt = case Col+2 of
+ Opt = case col2key(Col) of
Key -> Opt0#opt{sort_incr=not Bool};
NewKey -> Opt0#opt{sort_key=NewKey}
end,
Tables = get_tables(Node, Opt),
- Tabs = update_grid(Grid, Opt, Tables),
+ {Tabs,Sel} = update_grid(Grid, sel(State), Opt, Tables),
wxWindow:setFocus(Grid),
- {noreply, State#state{opt=Opt, tabs=Tabs}};
+ {noreply, State#state{opt=Opt, tabs=Tabs, selected=Sel}};
handle_event(#wx{id=Id}, State = #state{node=Node, grid=Grid, opt=Opt0})
when Id >= ?ID_ETS, Id =< ?ID_SYSTEM_TABLES ->
@@ -129,9 +135,9 @@ handle_event(#wx{id=Id}, State = #state{node=Node, grid=Grid, opt=Opt0})
self() ! Error,
{noreply, State};
Tables ->
- Tabs = update_grid(Grid, Opt, Tables),
+ {Tabs, Sel} = update_grid(Grid, sel(State), Opt, Tables),
wxWindow:setFocus(Grid),
- {noreply, State#state{opt=Opt, tabs=Tabs}}
+ {noreply, State#state{opt=Opt, tabs=Tabs, selected=Sel}}
end;
handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
@@ -202,6 +208,12 @@ handle_event(Event, _State) ->
handle_sync_event(_Event, _Obj, _State) ->
ok.
+handle_call(get_config, _, #state{timer=Timer, opt=Opt}=State) ->
+ #opt{type=Type, sys_hidden=Sys, unread_hidden=Unread} = Opt,
+ Conf0 = observer_lib:timer_config(Timer),
+ Conf = Conf0#{type=>Type, sys_hidden=>Sys, unread_hidden=>Unread},
+ {reply, Conf, State};
+
handle_call(Event, From, _State) ->
error({unhandled_call, Event, From}).
@@ -215,8 +227,9 @@ handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
%% no change
{noreply, State};
Tables ->
- Tabs = update_grid(Grid, Opt, Tables),
- {noreply, State#state{tabs=Tabs}}
+ {Tabs, Sel} = update_grid(Grid, sel(State), Opt, Tables),
+ Sel =/= undefined andalso wxListCtrl:ensureVisible(Grid, Sel),
+ {noreply, State#state{tabs=Tabs, selected=Sel}}
end;
handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt0,
@@ -228,11 +241,11 @@ handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt0,
Opt1 = Opt0#opt{type=ets},
{get_tables(Node, Opt1), Opt1}
end,
- Tabs = update_grid(Grid, Opt, Tables),
+ {Tabs,Sel} = update_grid(Grid, sel(State), Opt, Tables),
wxWindow:setFocus(Grid),
create_menus(Parent, Opt),
- Timer = observer_lib:start_timer(Timer0),
- {noreply, State#state{node=Node, tabs=Tabs, timer=Timer, opt=Opt}};
+ Timer = observer_lib:start_timer(Timer0, 10),
+ {noreply, State#state{node=Node, tabs=Tabs, timer=Timer, opt=Opt, selected=Sel}};
handle_info(not_active, State = #state{timer = Timer0}) ->
Timer = observer_lib:stop_timer(Timer0),
@@ -296,6 +309,13 @@ get_tables2(Node, #opt{type=Type, sys_hidden=Sys, unread_hidden=Unread}) ->
[list_to_tabrec(Tab) || Tab <- Result]
end.
+col2key(0) -> #tab.name;
+col2key(1) -> #tab.size;
+col2key(2) -> #tab.memory;
+col2key(3) -> #tab.owner;
+col2key(4) -> #tab.reg_name;
+col2key(5) -> #tab.id.
+
list_to_tabrec(PL) ->
#tab{name = proplists:get_value(name, PL),
id = proplists:get_value(id, PL, ignore),
@@ -366,13 +386,15 @@ list_to_strings([A]) -> integer_to_list(A);
list_to_strings([A|B]) ->
integer_to_list(A) ++ " ," ++ list_to_strings(B).
-update_grid(Grid, Opt, Tables) ->
- wx:batch(fun() -> update_grid2(Grid, Opt, Tables) end).
-update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Tables) ->
+update_grid(Grid, Selected, Opt, Tables) ->
+ wx:batch(fun() -> update_grid2(Grid, Selected, Opt, Tables) end).
+
+update_grid2(Grid, {SelName,SelId}, #opt{sort_key=Sort,sort_incr=Dir}, Tables) ->
wxListCtrl:deleteAllItems(Grid),
Update =
fun(#tab{name = Name, id = Id, owner = Owner, size = Size, memory = Memory,
- protection = Protection, reg_name = RegName}, Row) ->
+ protection = Protection, reg_name = RegName},
+ {Row, Sel}) ->
_Item = wxListCtrl:insertItem(Grid, Row, ""),
if (Row rem 2) =:= 0 ->
wxListCtrl:setItemBackgroundColour(Grid, Row, ?BG_EVEN);
@@ -389,11 +411,24 @@ update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Tables) ->
end,
[{0,Name}, {1,Size}, {2, Memory div 1024},
{3,Owner}, {4,RegName}, {5,Id}]),
- Row + 1
+ if SelName =:= Name, SelId =:= Id ->
+ wxListCtrl:setItemState(Grid, Row, 16#FFFF, ?wxLIST_STATE_SELECTED),
+ {Row+1, Row};
+ true ->
+ wxListCtrl:setItemState(Grid, Row, 0, ?wxLIST_STATE_SELECTED),
+ {Row+1, Sel}
+ end
end,
ProcInfo = case Dir of
false -> lists:reverse(lists:keysort(Sort, Tables));
true -> lists:keysort(Sort, Tables)
end,
- lists:foldl(Update, 0, ProcInfo),
- ProcInfo.
+ {_, Sel} = lists:foldl(Update, {0, undefined}, ProcInfo),
+ {ProcInfo, Sel}.
+
+sel(#state{selected=Sel, tabs=Tabs}) ->
+ try lists:nth(Sel+1, Tabs) of
+ #tab{name=Name, id=Id} -> {Name, Id}
+ catch _:_ ->
+ {undefined, undefined}
+ end.
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 83de4fa64c..0a591babdd 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -54,20 +54,14 @@
status_bar,
notebook,
main_panel,
- pro_panel,
- port_panel,
- tv_panel,
- sys_panel,
- trace_panel,
- app_panel,
- perf_panel,
- allc_panel,
+ panels,
active_tab,
node,
nodes,
prev_node="",
log = false,
- reply_to=false
+ reply_to=false,
+ config
}).
start() ->
@@ -118,6 +112,10 @@ init(_Args) ->
setup(#state{frame = Frame} = State) ->
%% Setup Menubar & Menus
+ Config = load_config(),
+ Cnf = fun(Who) ->
+ proplists:get_value(Who, Config, #{})
+ end,
MenuBar = wxMenuBar:new(),
{Nodes, NodeMenus} = get_nodes(),
@@ -131,7 +129,7 @@ setup(#state{frame = Frame} = State) ->
Notebook = wxNotebook:new(Panel, ?ID_NOTEBOOK, [{style, ?wxBK_DEFAULT}]),
%% System Panel
- SysPanel = observer_sys_wx:start_link(Notebook, self()),
+ SysPanel = observer_sys_wx:start_link(Notebook, self(), Cnf(sys_panel)),
wxNotebook:addPage(Notebook, SysPanel, "System", []),
%% Setup sizer create early to get it when window shows
@@ -145,43 +143,44 @@ setup(#state{frame = Frame} = State) ->
wxFrame:setTitle(Frame, atom_to_list(node())),
wxStatusBar:setStatusText(StatusBar, atom_to_list(node())),
- wxNotebook:connect(Notebook, command_notebook_page_changing),
- wxFrame:connect(Frame, close_window, [{skip, true}]),
+ wxNotebook:connect(Notebook, command_notebook_page_changed, [{skip, true}]),
+ wxFrame:connect(Frame, close_window, []),
wxMenu:connect(Frame, command_menu_selected),
wxFrame:show(Frame),
%% Freeze and thaw is buggy currently
- DoFreeze = [?wxMAJOR_VERSION,?wxMINOR_VERSION] < [2,9],
+ DoFreeze = [?wxMAJOR_VERSION,?wxMINOR_VERSION] < [2,9]
+ orelse element(1, os:type()) =:= win32,
DoFreeze andalso wxWindow:freeze(Panel),
%% I postpone the creation of the other tabs so they can query/use
%% the window size
%% Perf Viewer Panel
- PerfPanel = observer_perf_wx:start_link(Notebook, self()),
+ PerfPanel = observer_perf_wx:start_link(Notebook, self(), Cnf(perf_panel)),
wxNotebook:addPage(Notebook, PerfPanel, "Load Charts", []),
%% Memory Allocator Viewer Panel
- AllcPanel = observer_alloc_wx:start_link(Notebook, self()),
+ AllcPanel = observer_alloc_wx:start_link(Notebook, self(), Cnf(allc_panel)),
wxNotebook:addPage(Notebook, AllcPanel, ?ALLOC_STR, []),
%% App Viewer Panel
- AppPanel = observer_app_wx:start_link(Notebook, self()),
+ AppPanel = observer_app_wx:start_link(Notebook, self(), Cnf(app_panel)),
wxNotebook:addPage(Notebook, AppPanel, "Applications", []),
%% Process Panel
- ProPanel = observer_pro_wx:start_link(Notebook, self()),
+ ProPanel = observer_pro_wx:start_link(Notebook, self(), Cnf(pro_panel)),
wxNotebook:addPage(Notebook, ProPanel, "Processes", []),
%% Port Panel
- PortPanel = observer_port_wx:start_link(Notebook, self()),
+ PortPanel = observer_port_wx:start_link(Notebook, self(), Cnf(port_panel)),
wxNotebook:addPage(Notebook, PortPanel, "Ports", []),
%% Table Viewer Panel
- TVPanel = observer_tv_wx:start_link(Notebook, self()),
+ TVPanel = observer_tv_wx:start_link(Notebook, self(), Cnf(tv_panel)),
wxNotebook:addPage(Notebook, TVPanel, "Table Viewer", []),
%% Trace Viewer Panel
- TracePanel = observer_trace_wx:start_link(Notebook, self()),
+ TracePanel = observer_trace_wx:start_link(Notebook, self(), Cnf(trace_panel)),
wxNotebook:addPage(Notebook, TracePanel, ?TRACE_STR, []),
%% Force redraw (windows needs it)
@@ -193,19 +192,21 @@ setup(#state{frame = Frame} = State) ->
SysPid = wx_object:get_pid(SysPanel),
SysPid ! {active, node()},
+ Panels = [{sys_panel, SysPanel, "System"}, %% In order
+ {perf_panel, PerfPanel, "Load Charts"},
+ {allc_panel, AllcPanel, ?ALLOC_STR},
+ {app_panel, AppPanel, "Applications"},
+ {pro_panel, ProPanel, "Processes"},
+ {port_panel, PortPanel, "Ports"},
+ {tv_panel, TVPanel, "Table Viewer"},
+ {trace_panel, TracePanel, ?TRACE_STR}],
+
UpdState = State#state{main_panel = Panel,
notebook = Notebook,
menubar = MenuBar,
status_bar = StatusBar,
- sys_panel = SysPanel,
- pro_panel = ProPanel,
- port_panel = PortPanel,
- tv_panel = TVPanel,
- trace_panel = TracePanel,
- app_panel = AppPanel,
- perf_panel = PerfPanel,
- allc_panel = AllcPanel,
active_tab = SysPid,
+ panels = Panels,
node = node(),
nodes = Nodes
},
@@ -228,10 +229,12 @@ setup(#state{frame = Frame} = State) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%Callbacks
-handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}},
- #state{active_tab=Previous, node=Node} = State) ->
- case get_active_pid(State) of
- Previous -> {noreply, State};
+handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changed, nSel=Next}},
+ #state{active_tab=Previous, node=Node, panels=Panels} = State) ->
+ {_, Obj, _} = lists:nth(Next+1, Panels),
+ case wx_object:get_pid(Obj) of
+ Previous ->
+ {noreply, State};
Pid ->
Previous ! not_active,
Pid ! {active, Node},
@@ -362,8 +365,7 @@ handle_event(#wx{id = Id, event = #wxCommand{type = command_menu_selected}},
end,
{noreply, change_node_view(Node, LState)};
-handle_event(Event, State) ->
- Pid = get_active_pid(State),
+handle_event(Event, #state{active_tab=Pid} = State) ->
Pid ! Event,
{noreply, State}.
@@ -388,7 +390,8 @@ handle_call({create_menus, TabMenus}, _From,
handle_call({get_attrib, Attrib}, _From, State) ->
{reply, get(Attrib), State};
-handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) ->
+handle_call(get_tracer, _From, State=#state{panels=Panels}) ->
+ {_, TraceP, _} = lists:keyfind(trace_panel, 1, Panels),
{reply, TraceP, State};
handle_call(get_active_node, _From, State=#state{node=Node}) ->
@@ -424,9 +427,7 @@ handle_info({nodedown, Node},
create_txt_dialog(Frame, Msg, "Node down", ?wxICON_EXCLAMATION),
{noreply, State3};
-handle_info({open_link, Id0}, State = #state{pro_panel=ProcViewer,
- port_panel=PortViewer,
- frame=Frame}) ->
+handle_info({open_link, Id0}, State = #state{panels=Panels,frame=Frame}) ->
Id = case Id0 of
[_|_] -> try list_to_pid(Id0) catch _:_ -> Id0 end;
_ -> Id0
@@ -434,8 +435,10 @@ handle_info({open_link, Id0}, State = #state{pro_panel=ProcViewer,
%% Forward to process tab
case Id of
Pid when is_pid(Pid) ->
+ {pro_panel, ProcViewer, _} = lists:keyfind(pro_panel, 1, Panels),
wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid};
"#Port" ++ _ = Port ->
+ {port_panel, PortViewer, _} = lists:keyfind(port_panel, 1, Panels),
wx_object:get_pid(PortViewer) ! {portinfo_open, Port};
_ ->
Msg = io_lib:format("Information about ~p is not available or implemented",[Id]),
@@ -465,15 +468,13 @@ handle_info({stop, Me}, State) when Me =:= self() ->
handle_info(_Info, State) ->
{noreply, State}.
-stop_servers(#state{node=Node, log=LogOn, sys_panel=Sys, pro_panel=Procs, tv_panel=TVs,
- trace_panel=Trace, app_panel=Apps, perf_panel=Perfs,
- allc_panel=Alloc, port_panel=Ports} = _State) ->
+stop_servers(#state{node=Node, log=LogOn, panels=Panels} = _State) ->
LogOn andalso rpc:block_call(Node, rb, stop, []),
Me = self(),
- Tabs = [Sys, Procs, Ports, TVs, Trace, Apps, Perfs, Alloc],
+ save_config(Panels),
Stop = fun() ->
try
- _ = [wx_object:stop(Panel) || Panel <- Tabs],
+ _ = [wx_object:stop(Panel) || {_, Panel, _} <- Panels],
ok
catch _:_ -> ok
end,
@@ -490,6 +491,27 @@ terminate(_Reason, #state{frame = Frame, reply_to=From}) ->
end,
ok.
+load_config() ->
+ case file:consult(config_file()) of
+ {ok, Config} -> Config;
+ _ -> []
+ end.
+
+save_config(Panels) ->
+ Configs = [{Name, wx_object:call(Panel, get_config)} || {Name, Panel, _} <- Panels],
+ File = config_file(),
+ case filelib:ensure_dir(File) of
+ ok ->
+ Format = [io_lib:format("~p.~n",[Conf]) || Conf <- Configs],
+ _ = file:write_file(File, Format);
+ _ ->
+ ignore
+ end.
+
+config_file() ->
+ Dir = filename:basedir(user_config, "erl_observer"),
+ filename:join(Dir, "config.txt").
+
code_change(_, _, State) ->
{ok, State}.
@@ -549,8 +571,7 @@ connect2(NodeName, Opts, Cookie) ->
{error, net_kernel, Reason}
end.
-change_node_view(Node, State) ->
- Tab = get_active_pid(State),
+change_node_view(Node, #state{active_tab=Tab} = State) ->
Tab ! not_active,
Tab ! {active, Node},
StatusText = ["Observer - " | atom_to_list(Node)],
@@ -562,38 +583,13 @@ check_page_title(Notebook) ->
Selection = wxNotebook:getSelection(Notebook),
wxNotebook:getPageText(Notebook, Selection).
-get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys,
- tv_panel=Tv, trace_panel=Trace, app_panel=App,
- perf_panel=Perf, allc_panel=Alloc, port_panel=Port
- }) ->
- Panel = case check_page_title(Notebook) of
- "Processes" -> Pro;
- "Ports" -> Port;
- "System" -> Sys;
- "Table Viewer" -> Tv;
- ?TRACE_STR -> Trace;
- "Load Charts" -> Perf;
- "Applications" -> App;
- ?ALLOC_STR -> Alloc
- end,
- wx_object:get_pid(Panel).
-
-pid2panel(Pid, #state{pro_panel=Pro, sys_panel=Sys,
- tv_panel=Tv, trace_panel=Trace, app_panel=App,
- perf_panel=Perf, allc_panel=Alloc, port_panel=Port}) ->
- case Pid of
- Pro -> "Processes";
- Port -> "Ports";
- Sys -> "System";
- Tv -> "Table Viewer" ;
- Trace -> ?TRACE_STR;
- Perf -> "Load Charts";
- App -> "Applications";
- Alloc -> ?ALLOC_STR;
- _ -> "unknown"
+pid2panel(Pid, #state{panels=Panels}) ->
+ PanelPids = [{Name, wx_object:get_pid(Obj)} || {Name, Obj, _} <- Panels],
+ case lists:keyfind(Pid, 2, PanelPids) of
+ false -> "unknown";
+ {Name,_} -> Name
end.
-
create_connect_dialog(ping, #state{frame = Frame, prev_node=Prev}) ->
Dialog = wxTextEntryDialog:new(Frame, "Connect to node", [{value, Prev}]),
case wxDialog:showModal(Dialog) of
diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl
index 58c5a773c3..5fe62a46f6 100644
--- a/lib/runtime_tools/src/dyntrace.erl
+++ b/lib/runtime_tools/src/dyntrace.erl
@@ -61,8 +61,8 @@
enabled_garbage_collection/3,
enabled/3]).
-
-export([user_trace_i4s4/9]). % Know what you're doing!
+-compile(no_native).
-on_load(on_load/0).
-type probe_arg() :: integer() | iolist().
diff --git a/lib/stdlib/doc/src/assert_hrl.xml b/lib/stdlib/doc/src/assert_hrl.xml
index 57bb5207df..ea23cca2ee 100644
--- a/lib/stdlib/doc/src/assert_hrl.xml
+++ b/lib/stdlib/doc/src/assert_hrl.xml
@@ -4,7 +4,7 @@
<fileref>
<header>
<copyright>
- <year>2012</year><year>2016</year>
+ <year>2012</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -92,18 +92,21 @@ erlc -DNOASSERT=true *.erl</code>
<title>Macros</title>
<taglist>
<tag><c>assert(BoolExpr)</c></tag>
- <tag><c>assert(BoolExpr, Comment)</c></tag>
+ <item></item>
+ <tag><c>URKAassert(BoolExpr, Comment)</c></tag>
<item>
<p>Tests that <c>BoolExpr</c> completes normally returning
<c>true</c>.</p>
</item>
<tag><c>assertNot(BoolExpr)</c></tag>
+ <item></item>
<tag><c>assertNot(BoolExpr, Comment)</c></tag>
<item>
<p>Tests that <c>BoolExpr</c> completes normally returning
<c>false</c>.</p>
</item>
<tag><c>assertMatch(GuardedPattern, Expr)</c></tag>
+ <item></item>
<tag><c>assertMatch(GuardedPattern, Expr, Comment)</c></tag>
<item>
<p>Tests that <c>Expr</c> completes normally yielding a value that
@@ -115,6 +118,7 @@ erlc -DNOASSERT=true *.erl</code>
?assertMatch({bork, X} when X > 0, f())</code>
</item>
<tag><c>assertNotMatch(GuardedPattern, Expr)</c></tag>
+ <item></item>
<tag><c>assertNotMatch(GuardedPattern, Expr, Comment)</c></tag>
<item>
<p>Tests that <c>Expr</c> completes normally yielding a value that does
@@ -123,18 +127,21 @@ erlc -DNOASSERT=true *.erl</code>
<c>when</c> part.</p>
</item>
<tag><c>assertEqual(ExpectedValue, Expr)</c></tag>
+ <item></item>
<tag><c>assertEqual(ExpectedValue, Expr, Comment)</c></tag>
<item>
<p>Tests that <c>Expr</c> completes normally yielding a value that is
exactly equal to <c>ExpectedValue</c>.</p>
</item>
<tag><c>assertNotEqual(ExpectedValue, Expr)</c></tag>
+ <item></item>
<tag><c>assertNotEqual(ExpectedValue, Expr, Comment)</c></tag>
<item>
<p>Tests that <c>Expr</c> completes normally yielding a value that is
not exactly equal to <c>ExpectedValue</c>.</p>
</item>
<tag><c>assertException(Class, Term, Expr)</c></tag>
+ <item></item>
<tag><c>assertException(Class, Term, Expr, Comment)</c></tag>
<item>
<p>Tests that <c>Expr</c> completes abnormally with an exception of type
@@ -145,6 +152,7 @@ erlc -DNOASSERT=true *.erl</code>
patterns, as in <c>assertMatch</c>.</p>
</item>
<tag><c>assertNotException(Class, Term, Expr)</c></tag>
+ <item></item>
<tag><c>assertNotException(Class, Term, Expr, Comment)</c></tag>
<item>
<p>Tests that <c>Expr</c> does not evaluate abnormally with an
@@ -155,16 +163,19 @@ erlc -DNOASSERT=true *.erl</code>
be guarded patterns.</p>
</item>
<tag><c>assertError(Term, Expr)</c></tag>
+ <item></item>
<tag><c>assertError(Term, Expr, Comment)</c></tag>
<item>
<p>Equivalent to <c>assertException(error, Term, Expr)</c></p>
</item>
<tag><c>assertExit(Term, Expr)</c></tag>
+ <item></item>
<tag><c>assertExit(Term, Expr, Comment)</c></tag>
<item>
<p>Equivalent to <c>assertException(exit, Term, Expr)</c></p>
</item>
<tag><c>assertThrow(Term, Expr)</c></tag>
+ <item></item>
<tag><c>assertThrow(Term, Expr, Comment)</c></tag>
<item>
<p>Equivalent to <c>assertException(throw, Term, Expr)</c></p>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 05401a2d40..d1ec176f81 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -1491,6 +1491,25 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</func>
<func>
+ <name name="select_replace" arity="2"/>
+ <fsummary>Match and replace objects atomically in an ETS table</fsummary>
+ <desc>
+ <p>Matches the objects in the table <c><anno>Tab</anno></c> using a
+ <seealso marker="#match_spec">match specification</seealso>. If
+ an object is matched, the existing object is replaced with
+ the match specification result, which <em>must</em> retain
+ the original key or the operation will fail with <c>badarg</c>.</p>
+ <p>For the moment, due to performance and semantic constraints,
+ tables of type <c>bag</c> are not yet supported.</p>
+ <p>The function returns the total number of replaced objects.</p>
+ <note>
+ <p>The match/replacement operation atomicity scope is limited
+ to each individual object.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
<name name="select_reverse" arity="1"/>
<fsummary>Continue matching objects in an ETS table.</fsummary>
<desc>
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 90e19e6b9f..195a407570 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -70,7 +70,7 @@
match_object/2, match_object/3, match_spec_compile/1,
match_spec_run_r/3, member/2, new/2, next/2, prev/2,
rename/2, safe_fixtable/2, select/1, select/2, select/3,
- select_count/2, select_delete/2, select_reverse/1,
+ select_count/2, select_delete/2, select_replace/2, select_reverse/1,
select_reverse/2, select_reverse/3, setopts/2, slot/2,
take/2,
update_counter/3, update_counter/4, update_element/3]).
@@ -379,6 +379,14 @@ select_count(_, _) ->
select_delete(_, _) ->
erlang:nif_error(undef).
+-spec select_replace(Tab, MatchSpec) -> NumReplaced when
+ Tab :: tab(),
+ MatchSpec :: match_spec(),
+ NumReplaced :: non_neg_integer().
+
+select_replace(_, _) ->
+ erlang:nif_error(undef).
+
-spec select_reverse(Tab, MatchSpec) -> [Match] when
Tab :: tab(),
MatchSpec :: match_spec(),
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index ebf7dbff62..ac68fdcc34 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -39,8 +39,9 @@
-export([lookup_element_mult/1]).
-export([foldl_ordered/1, foldr_ordered/1, foldl/1, foldr/1, fold_empty/1]).
-export([t_delete_object/1, t_init_table/1, t_whitebox/1,
+ select_bound_chunk/1,
t_delete_all_objects/1, t_insert_list/1, t_test_ms/1,
- t_select_delete/1,t_ets_dets/1]).
+ t_select_delete/1,t_select_replace/1,t_ets_dets/1]).
-export([ordered/1, ordered_match/1, interface_equality/1,
fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1,
@@ -64,7 +65,7 @@
meta_lookup_named_read/1, meta_lookup_named_write/1,
meta_newdel_unnamed/1, meta_newdel_named/1]).
-export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1,
- otp_8166/1, otp_8732/1]).
+ smp_select_replace/1, otp_8166/1, otp_8732/1]).
-export([exit_large_table_owner/1,
exit_many_large_table_owner/1,
exit_many_tables_owner/1,
@@ -87,6 +88,7 @@
-include_lib("common_test/include/ct.hrl").
-define(m(A,B), assert_eq(A,B)).
+-define(heap_binary_size, 64).
init_per_testcase(Case, Config) ->
rand:seed(exsplus),
@@ -117,15 +119,16 @@ all() ->
update_counter_with_default, partly_bound,
update_counter_table_growth,
match_heavy, {group, fold}, member, t_delete_object,
+ select_bound_chunk,
t_init_table, t_whitebox, t_delete_all_objects,
- t_insert_list, t_test_ms, t_select_delete, t_ets_dets,
- memory, t_select_reverse, t_bucket_disappears,
+ t_insert_list, t_test_ms, t_select_delete, t_select_replace,
+ t_ets_dets, memory, t_select_reverse, t_bucket_disappears,
select_fail, t_insert_new, t_repair_continuation,
otp_5340, otp_6338, otp_6842_select_1000, otp_7665,
otp_8732, meta_wb, grow_shrink, grow_pseudo_deleted,
shrink_pseudo_deleted, {group, meta_smp}, smp_insert,
- smp_fixed_delete, smp_unfix_fix, smp_select_delete,
- otp_8166, exit_large_table_owner,
+ smp_fixed_delete, smp_unfix_fix, smp_select_replace,
+ smp_select_delete, otp_8166, exit_large_table_owner,
exit_many_large_table_owner, exit_many_tables_owner,
exit_many_many_tables_owner, write_concurrency, heir,
give_away, setopts, bad_table, types,
@@ -695,6 +698,15 @@ whitebox_2(Opts) ->
ets:delete(T2),
ok.
+select_bound_chunk(Config) ->
+ repeat_for_opts(fun select_bound_chunk_do/1, [all_types]).
+
+select_bound_chunk_do(Opts) ->
+ T = ets:new(x, Opts),
+ ets:insert(T, [{key, 1}]),
+ {[{key, 1}], '$end_of_table'} = ets:select(T, [{{key,1},[],['$_']}], 100000),
+ ok.
+
%% Test ets:to/from_dets.
t_ets_dets(Config) when is_list(Config) ->
@@ -1136,6 +1148,211 @@ t_select_delete(Config) when is_list(Config) ->
lists:foreach(fun(Tab) -> ets:delete(Tab) end,Tables),
verify_etsmem(EtsMem).
+%% Tests the ets:select_replace/2 BIF
+t_select_replace(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ Tables = fill_sets_int(10000) ++ fill_sets_int(10000, [{write_concurrency,true}]),
+
+ TestFun = fun (Table, TableType) when TableType =:= bag ->
+ % Operation not supported; bag implementation
+ % presented both semantic consistency and performance issues.
+ 10000 = ets:select_delete(Table, [{'_',[],[true]}]);
+
+ (Table, TableType) ->
+ % Invalid replacement doesn't keep the key
+ MatchSpec1 = [{{'$1', '$2'},
+ [{'=:=', {'band', '$1', 2#11}, 2#11},
+ {'=/=', {'hd', '$2'}, $x}],
+ [{{'$2', '$1'}}]}],
+ {'EXIT',{badarg,_}} = (catch ets:select_replace(Table, MatchSpec1)),
+
+ % Invalid replacement doesn't keep the key (even though it would be the same value)
+ MatchSpec2 = [{{'$1', '$2'},
+ [{'=:=', {'band', '$1', 2#11}, 2#11}],
+ [{{{'+', '$1', 0}, '$2'}}]},
+ {{'$1', '$2'},
+ [{'=/=', {'band', '$1', 2#11}, 2#11}],
+ [{{{'-', '$1', 0}, '$2'}}]}],
+ {'EXIT',{badarg,_}} = (catch ets:select_replace(Table, MatchSpec2)),
+
+ % Invalid replacement changes key to float equivalent
+ MatchSpec3 = [{{'$1', '$2'},
+ [{'=:=', {'band', '$1', 2#11}, 2#11},
+ {'=/=', {'hd', '$2'}, $x}],
+ [{{{'*', '$1', 1.0}, '$2'}}]}],
+ {'EXIT',{badarg,_}} = (catch ets:select_replace(Table, MatchSpec3)),
+
+ % Replacements are differently-sized tuples
+ MatchSpec4_A = [{{'$1','$2'},
+ [{'<', {'rem', '$1', 5}, 2}],
+ [{{'$1', [$x | '$2'], stuff}}]}],
+ MatchSpec4_B = [{{'$1','$2','_'},
+ [],
+ [{{'$1','$2'}}]}],
+ 4000 = ets:select_replace(Table, MatchSpec4_A),
+ 4000 = ets:select_replace(Table, MatchSpec4_B),
+
+ % Replacement is the same tuple
+ MatchSpec5 = [{{'$1', '$2'},
+ [{'>', {'rem', '$1', 5}, 3}],
+ ['$_']}],
+ 2000 = ets:select_replace(Table, MatchSpec5),
+
+ % Replacement reconstructs an equal tuple
+ MatchSpec6 = [{{'$1', '$2'},
+ [{'>', {'rem', '$1', 5}, 3}],
+ [{{'$1', '$2'}}]}],
+ 2000 = ets:select_replace(Table, MatchSpec6),
+
+ % Replacement uses {element,KeyPos,T} for key
+ 2000 = ets:select_replace(Table,
+ [{{'$1', '$2'},
+ [{'>', {'rem', '$1', 5}, 3}],
+ [{{{element, 1, '$_'}, '$2'}}]}]),
+
+ % Replacement uses wrong {element,KeyPos,T} for key
+ {'EXIT',{badarg,_}} = (catch ets:select_replace(Table,
+ [{{'$1', '$2'},
+ [],
+ [{{{element, 2, '$_'}, '$2'}}]}])),
+
+ check(Table,
+ fun ({N, [$x, C | _]}) when ((N rem 5) < 2) -> (C >= $0) andalso (C =< $9);
+ ({N, [C | _]}) when is_float(N) -> (C >= $0) andalso (C =< $9);
+ ({N, [C | _]}) when ((N rem 5) > 3) -> (C >= $0) andalso (C =< $9);
+ ({_, [C | _]}) -> (C >= $0) andalso (C =< $9)
+ end,
+ 10000),
+
+ % Replace unbound range (>)
+ MatchSpec7 = [{{'$1', '$2'},
+ [{'>', '$1', 7000}],
+ [{{'$1', {{gt_range, '$2'}}}}]}],
+ 3000 = ets:select_replace(Table, MatchSpec7),
+
+ % Replace unbound range (<)
+ MatchSpec8 = [{{'$1', '$2'},
+ [{'<', '$1', 3000}],
+ [{{'$1', {{le_range, '$2'}}}}]}],
+ case TableType of
+ ordered_set -> 2999 = ets:select_replace(Table, MatchSpec8);
+ set -> 2999 = ets:select_replace(Table, MatchSpec8);
+ duplicate_bag -> 2998 = ets:select_replace(Table, MatchSpec8)
+ end,
+
+ % Replace bound range
+ MatchSpec9 = [{{'$1', '$2'},
+ [{'>=', '$1', 3001},
+ {'<', '$1', 7000}],
+ [{{'$1', {{range, '$2'}}}}]}],
+ case TableType of
+ ordered_set -> 3999 = ets:select_replace(Table, MatchSpec9);
+ set -> 3999 = ets:select_replace(Table, MatchSpec9);
+ duplicate_bag -> 3998 = ets:select_replace(Table, MatchSpec9)
+ end,
+
+ % Replace particular keys
+ MatchSpec10 = [{{'$1', '$2'},
+ [{'==', '$1', 3000}],
+ [{{'$1', {{specific1, '$2'}}}}]},
+ {{'$1', '$2'},
+ [{'==', '$1', 7000}],
+ [{{'$1', {{specific2, '$2'}}}}]}],
+ case TableType of
+ ordered_set -> 2 = ets:select_replace(Table, MatchSpec10);
+ set -> 2 = ets:select_replace(Table, MatchSpec10);
+ duplicate_bag -> 4 = ets:select_replace(Table, MatchSpec10)
+ end,
+
+ check(Table,
+ fun ({N, {gt_range, _}}) -> N > 7000;
+ ({N, {le_range, _}}) -> N < 3000;
+ ({N, {range, _}}) -> (N >= 3001) andalso (N < 7000);
+ ({N, {specific1, _}}) -> N == 3000;
+ ({N, {specific2, _}}) -> N == 7000
+ end,
+ 10000),
+
+ 10000 = ets:select_delete(Table, [{'_',[],[true]}]),
+ check(Table, fun (_) -> false end, 0)
+ end,
+
+ lists:foreach(
+ fun(Table) ->
+ TestFun(Table, ets:info(Table, type)),
+ ets:delete(Table)
+ end,
+ Tables),
+
+ %% Test key-safe match-specs are accepted
+ BigNum = (123 bsl 123),
+ RefcBin = list_to_binary(lists:seq(1,?heap_binary_size+1)),
+ Terms = [a, "hej", 123, 1.23, BigNum , <<"123">>, RefcBin, TestFun, self()],
+ EqPairs = fun(X,Y) ->
+ [{ '$1', '$1'},
+ { {X, Y}, {{X, Y}}},
+ { {'$1', Y}, {{'$1', Y}}},
+ { {{X, Y}}, {{{{X, Y}}}}},
+ { {X}, {{X}}},
+ { X, {const, X}},
+ { {X,Y}, {const, {X,Y}}},
+ { {X}, {const, {X}}},
+ { {X, Y}, {{X, {const, Y}}}},
+ { {X, {Y,'$1'}}, {{{const, X}, {{Y,'$1'}}}}},
+ { [X, Y | '$1'], [X, Y | '$1']},
+ { [{X, '$1'}, Y], [{{X, '$1'}}, Y]},
+ { [{X, Y} | '$1'], [{const, {X, Y}} | '$1']},
+ { [$p,$r,$e,$f,$i,$x | '$1'], [$p,$r,$e,$f,$i,$x | '$1']},
+ { {[{X,Y}]}, {{[{{X,Y}}]}}},
+ { {[{X,Y}]}, {{{const, [{X,Y}]}}}},
+ { {[{X,Y}]}, {{[{const,{X,Y}}]}}}
+ ]
+ end,
+
+ T2 = ets:new(x, []),
+ [lists:foreach(fun({A, B}) ->
+ %% just check that matchspec is accepted
+ 0 = ets:select_replace(T2, [{{A, '$2', '$3'}, [], [{{B, '$3', '$2'}}]}])
+ end,
+ EqPairs(X,Y)) || X <- Terms, Y <- Terms],
+
+ %% Test key-unsafe matchspecs are rejected
+ NeqPairs = fun(X, Y) ->
+ [{'$1', '$2'},
+ {{X, Y}, {X, Y}},
+ {{{X, Y}}, {{{X, Y}}}},
+ {{X}, {{{X}}}},
+ {{const, X}, {const, X}},
+ {{const, {X,Y}}, {const, {X,Y}}},
+ {'$1', {const, '$1'}},
+ {{X}, {const, {{X}}}},
+ {{X, {Y,'$1'}}, {{{const, X}, {Y,'$1'}}}},
+ {[X, Y | '$1'], [X, Y]},
+ {[X, Y], [X, Y | '$1']},
+ {[{X, '$1'}, Y], [{X, '$1'}, Y]},
+ {[$p,$r,$e,$f,$i,$x | '$1'], [$p,$r,$e,$f,$I,$x | '$1']},
+ { {[{X,Y}]}, {{[{X,Y}]}}},
+ { {[{X,Y}]}, {{{const, [{{X,Y}}]}}}},
+ { {[{X,Y}]}, {{[{const,{{X,Y}}}]}}},
+ {'_', '_'},
+ {'$_', '$_'},
+ {'$$', '$$'},
+ {#{}, #{}},
+ {#{X => '$1'}, #{X => '$1'}}
+ ]
+ end,
+
+ [lists:foreach(fun({A, B}) ->
+ %% just check that matchspec is rejected
+ {'EXIT',{badarg,_}} = (catch ets:select_replace(T2, [{{A, '$2', '$3'}, [], [{{B, '$3', '$2'}}]}]))
+ end,
+ NeqPairs(X,Y)) || X <- Terms, Y <- Terms],
+
+
+ ets:delete(T2),
+
+ verify_etsmem(EtsMem).
+
%% Test that partly bound keys gives faster matches.
partly_bound(Config) when is_list(Config) ->
case os:type() of
@@ -5419,6 +5636,42 @@ smp_select_delete(Config) when is_list(Config) ->
false = ets:info(T,fixed),
ets:delete(T).
+smp_select_replace(Config) when is_list(Config) ->
+ lists:foreach(
+ fun (TableType) ->
+ T = ets_new(smp_select_replace, [TableType, named_table, public,
+ {write_concurrency, true}]),
+ WorkerCount = 20,
+ CounterIterations = 10000,
+ InitF = fun (_) -> no_state end,
+ ExecF = fun (State) ->
+ lists:foreach(
+ fun F(IterId) ->
+ CounterId = rand:uniform(WorkerCount),
+ Match = [{{'$1', '$2'},
+ [{'=:=', '$1', CounterId}],
+ [{{'$1', {'+', '$2', 1}}}]}],
+ case ets:select_replace(T, Match) of
+ 1 -> ok;
+ 0 ->
+ ets:insert_new(T, {CounterId, 1}) orelse
+ F(IterId)
+ end
+ end,
+ lists:seq(1, CounterIterations)),
+ State
+ end,
+ FiniF = fun (State) -> State end,
+ run_workers_do(InitF, ExecF, FiniF, WorkerCount),
+ FinalCounts = ets:select(T, [{{'_', '$1'}, [], ['$1']}]),
+ TotalIterations = WorkerCount * CounterIterations * erlang:system_info(schedulers),
+ TotalIterations = lists:sum(FinalCounts),
+ WorkerCount = ets:select_delete(T, [{{'_', '_'}, [], [true]}]),
+ 0 = ets:info(T, size),
+ ets:delete(T)
+ end,
+ [ordered_set, set, duplicate_bag]).
+
%% Test different types.
types(Config) when is_list(Config) ->
init_externals(),
@@ -5959,7 +6212,6 @@ only_if_smp(Schedulers, Func) ->
end.
%% Copy-paste from emulator/test/binary_SUITE.erl
--define(heap_binary_size, 64).
test_terms(Test_Func, Mode) ->
garbage_collect(),
Pib0 = process_info(self(),binary),