diff options
Diffstat (limited to 'lib')
50 files changed, 1184 insertions, 908 deletions
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 37e67d8630..6e41b01c44 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -783,6 +783,27 @@ be matched by corresponding &capability; configuration, of </item> +<marker id="incoming_maxlen"/> +<tag><c>{incoming_maxlen, 0..16777215}</c></tag> +<item> +<p> +Bound on the expected size of incoming Diameter messages. +Messages larger than the specified number of bytes are discarded.</p> + +<p> +Defaults to <c>16777215</c>, the maximum value of the 24-bit Message +Length field in a Diameter Header.</p> + +<warning> +<p> +This option should be set to as low a value as is sufficient for the +Diameter applications and peers in question, since decoding incoming +messages from a malicious peer can otherwise generate significant +load.</p> +</warning> + +</item> + <tag><c>{restrict_connections, false | node | nodes diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml index 9db9bcffde..5cf1b174a0 100644 --- a/lib/diameter/doc/src/diameter_dict.xml +++ b/lib/diameter/doc/src/diameter_dict.xml @@ -529,7 +529,7 @@ answer record and passed to a &app_handle_request; callback upon reception of an incoming request.</p> <p> -In cases in which there is a choice between list() and binary() types +In cases in which there is a choice between string() and binary() types for OctetString() and derived types, the representation is determined by the value of &mod_string_decode;.</p> diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl index 8272904856..0eef218a07 100644 --- a/lib/diameter/include/diameter_gen.hrl +++ b/lib/diameter/include/diameter_gen.hrl @@ -395,7 +395,7 @@ d(false, Reason, Name, Avp, {Avps, Acc}) -> diameter_lib:log(decode_error, ?MODULE, ?LINE, - {Reason, Name, Avp#diameter_avp.name, Stack}), + {Name, Avp#diameter_avp.name, Stack}), {Rec, Failed} = Acc, {[Avp|Avps], {Rec, [rc(Reason, Avp) | Failed]}}. diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index 67dfc7bdbf..010f977b97 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -45,6 +45,7 @@ -export_type([evaluable/0, restriction/0, + message_length/0, remotes/0, sequence/0, app_alias/0, @@ -298,6 +299,9 @@ call(SvcName, App, Message) -> | [node()] | evaluable(). +-type message_length() + :: 0..16#FFFFFF. + %% Options passed to start_service/2 -type service_opt() @@ -307,6 +311,7 @@ call(SvcName, App, Message) -> | {sequence, sequence() | evaluable()} | {share_peers, remotes()} | {string_decode, boolean()} + | {incoming_maxlen, message_length()} | {use_shared_peers, remotes()} | {spawn_opt, list()}. diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 0d0304bf33..8ac3b9d6ca 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -159,7 +159,8 @@ stop_service(SvcName) -> %% # add_transport/2 %% -------------------------------------------------------------------------- --spec add_transport(diameter:service_name(), {connect|listen, [diameter:transport_opt()]}) +-spec add_transport(diameter:service_name(), + {connect|listen, [diameter:transport_opt()]}) -> {ok, diameter:transport_ref()} | {error, term()}. @@ -645,6 +646,7 @@ make_config(SvcName, Opts) -> {false, monitor}, {?NOMASK, sequence}, {nodes, restrict_connections}, + {16#FFFFFF, incoming_maxlen}, {true, string_decode}, {[], spawn_opt}]), @@ -670,12 +672,20 @@ make_opts(Opts, Defs) -> [{K, opt(K,V)} || {K,V} <- Known]. +opt(incoming_maxlen, N) + when 0 =< N, N < 1 bsl 24 -> + N; + opt(spawn_opt, L) when is_list(L) -> L; opt(K, false = B) - when K /= sequence -> + when K == share_peers; + K == use_shared_peers; + K == monitor; + K == restrict_connections; + K == string_decode -> B; opt(K, true = B) diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index aac2685514..2255d0a76b 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -125,7 +125,8 @@ %% outgoing DPR; boolean says whether or not %% the request was sent explicitly with %% diameter:call/4. - length_errors :: exit | handle | discard}). + length_errors :: exit | handle | discard, + incoming_maxlen :: integer() | infinity}). %% There are non-3588 states possible as a consequence of 5.6.1 of the %% standard and the corresponding problem for incoming CEA's: we don't @@ -203,6 +204,7 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) -> diameter_stats:reg(Ref), diameter_codec:setopts([{common_dictionary, Dict0} | SvcOpts]), {_,_} = Mask = proplists:get_value(sequence, SvcOpts), + Maxlen = proplists:get_value(incoming_maxlen, SvcOpts, 16#FFFFFF), {[Cs,Ds], Rest} = proplists:split(Opts, [capabilities_cb, disconnect_cb]), putr(?CB_KEY, {Ref, [F || {_,F} <- Cs]}), putr(?DPR_KEY, [F || {_, F} <- Ds]), @@ -223,7 +225,8 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) -> dictionary = Dict0, mode = M, service = svc(Svc, Addrs), - length_errors = OnLengthErr}. + length_errors = OnLengthErr, + incoming_maxlen = Maxlen}. %% The transport returns its local ip addresses so that different %% transports on the same service can use different local addresses. %% The local addresses are put into Host-IP-Address avps here when @@ -326,11 +329,14 @@ handle_info(T, #state{} = State) -> {?MODULE, Tag, Reason} -> ?LOG(stop, Tag), {stop, {shutdown, Reason}, State} - end. + end; %% The form of the throw caught here is historical. It's %% significant that it's not a 2-tuple, as in ?FAILURE(Reason), %% since these are caught elsewhere. +handle_info(T, S) -> %% started in old code + handle_info(T, #state{} = erlang:append_element(S, infinity)). + %% Note that there's no guarantee that the service and transport %% capabilities are good enough to build a CER/CEA that can be %% succesfully encoded. It's not checked at diameter:add_transport/2 @@ -561,6 +567,12 @@ recv(Bin, S) -> %% recv1/3 +recv1(_, + #diameter_packet{header = H, bin = Bin}, + #state{incoming_maxlen = M}) + when M < size(Bin) -> + invalid(false, incoming_maxlen_exceeded, {size(Bin), H}); + %% Incoming request after outgoing DPR: discard. Don't discard DPR, so %% both ends don't do so when sending simultaneously. recv1(Name, diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index a01bcdd4e7..86e744dfbe 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -131,7 +131,8 @@ | {share_peers, diameter:remotes()} %% broadcast to | {use_shared_peers, diameter:remotes()} %% use from | {restrict_connections, diameter:restriction()} - | {string_decode, boolean()}]}). + | {string_decode, boolean()} + | {incoming_maxlen, diameter:message_length()}]}). %% shared_peers reflects the peers broadcast from remote nodes. %% Record representing an RFC 3539 watchdog process implemented by @@ -698,7 +699,8 @@ service_options(Opts) -> Opts, ?RESTRICT)}, {spawn_opt, proplists:get_value(spawn_opt, Opts, [])}, - {string_decode, proplists:get_value(string_decode, Opts, true)}]. + {string_decode, proplists:get_value(string_decode, Opts, true)}, + {incoming_maxlen, proplists:get_value(incoming_maxlen, Opts, 16#FFFFFF)}]. %% The order of options is significant since we match against the list. mref(false = No) -> diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 784f9ca08f..538ebeeeba 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -78,7 +78,11 @@ service_name :: diameter:service_name(), apps :: [#diameter_app{}], sequence :: diameter:sequence(), - codec :: list()}). + codec :: [{string_decode, boolean()} + | {incoming_maxlen, diameter:message_length()}]}). +%% Note that incoming_maxlen is currently handled in diameter_peer_fsm, +%% so that any message exceeding the maximum is discarded. Retain the +%% option in case we want to extend the values and semantics. %% Record stored in diameter_request for each outgoing request. -record(request, @@ -102,7 +106,9 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) -> peerT = PeerT, apps = Apps, sequence = Mask, - codec = [T || {K,_} = T <- SvcOpts, K == string_decode]}. + codec = [T || {K,_} = T <- SvcOpts, + lists:member(K, [string_decode, + incoming_maxlen])]}. %% --------------------------------------------------------------------------- %% peer_up/1 @@ -233,6 +239,8 @@ receive_message(TPid, Pkt, Dict0, RecvData) Dict0, RecvData). +%% recv/6 + %% Incoming request ... recv(true, false, TPid, Pkt, Dict0, T) -> spawn_request(TPid, Pkt, Dict0, T); @@ -240,6 +248,7 @@ recv(true, false, TPid, Pkt, Dict0, T) -> %% ... answer to known request ... recv(false, #request{ref = Ref, handler = Pid} = Req, _, Pkt, Dict0, _) -> Pid ! {answer, Ref, Req, Dict0, Pkt}; + %% Note that failover could have happened prior to this message being %% received and triggering failback. That is, both a failover message %% and answer may be on their way to the handler process. In the worst @@ -1693,6 +1702,8 @@ send({TPid, Pkt, #request{handler = Pid} = Req0, SvcName, Timeout, TRef}) -> end. %% recv/4 +%% +%% Relay an answer from a remote node. recv(TPid, Pid, TRef, Ref) -> receive diff --git a/lib/diameter/src/base/diameter_types.erl b/lib/diameter/src/base/diameter_types.erl index fe7613541c..87a0f0663d 100644 --- a/lib/diameter/src/base/diameter_types.erl +++ b/lib/diameter/src/base/diameter_types.erl @@ -93,7 +93,7 @@ case diameter_codec:getopt(string_decode) of true -> binary_to_list(Bin); - _ -> + false -> Bin end; @@ -565,18 +565,29 @@ msb(false) -> ?TIME_2036. scan_uri(Bin) -> RE = "^(aaas?)://" - "([-a-zA-Z0-9.]+)" - "(:([0-9]+))?" + "([-a-zA-Z0-9.]{1,255})" + "(:0{0,5}([0-9]{1,5}))?" "(;transport=(tcp|sctp|udp))?" "(;protocol=(diameter|radius|tacacs\\+))?$", + %% A port number is 16-bit, so an arbitrary number of digits is + %% just a vulnerability, but provide a little slack with leading + %% zeros in a port number just because the regexp was previously + %% [0-9]+ and it's not inconceivable that a value might be padded. + %% Don't fantasize about this padding being more than the number + %% of digits in the port number proper. + %% + %% Similarly, a FQDN can't be arbitrarily long: at most 255 + %% octets. {match, [A, DN, PN, T, P]} = re:run(Bin, RE, [{capture, [1,2,4,6,8], binary}]), Type = to_atom(A), {PN0, T0} = defaults(diameter_codec:getopt(rfc), Type), + PortNr = to_int(PN, PN0), + 0 = PortNr bsr 16, %% assert #diameter_uri{type = Type, - fqdn = from_bin(DN), - port = to_int(PN, PN0), + fqdn = 'OctetString'(decode, DN), + port = PortNr, transport = to_atom(T, T0), protocol = to_atom(P, diameter)}. @@ -588,14 +599,6 @@ defaults(6733, aaa) -> defaults(6733, aaas) -> {5658, tcp}. -from_bin(B) -> - case diameter_codec:getopt(string_decode) of - true -> - binary_to_list(B); - false -> - B - end. - to_int(<<>>, N) -> N; to_int(B, _) -> diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl index 854b71ba93..5f1dbfbd61 100644 --- a/lib/diameter/test/diameter_codec_test.erl +++ b/lib/diameter/test/diameter_codec_test.erl @@ -352,15 +352,19 @@ values('DiameterURI') -> {[], ["aaa" ++ S ++ "://diameter.se" ++ P ++ Tr ++ Pr || S <- ["", "s"], - P <- ["", ":1234"], + P <- ["", ":1234", ":0", ":65535"], Tr <- ["" | [";transport=" ++ X || X <- ["tcp", "sctp", "udp"]]], Pr <- ["" | [";protocol=" ++ X || X <- ["diameter","radius","tacacs+"]]], Tr /= ";transport=udp" - orelse (Pr /= ";protocol=diameter" andalso Pr /= "")], - ["aaa://diameter.se;transport=udp;protocol=diameter", + orelse (Pr /= ";protocol=diameter" andalso Pr /= "")] + ++ ["aaa://" ++ lists:duplicate(255, $x)], + ["aaa://diameter.se:65536", + "aaa://diameter.se:-1", + "aaa://diameter.se;transport=udp;protocol=diameter", "aaa://diameter.se;transport=udp", + "aaa://" ++ lists:duplicate(256, $x), "aaa://:3868", "aaax://diameter.se", "aaa://diameter.se;transport=tcpx", diff --git a/lib/diameter/test/diameter_config_SUITE.erl b/lib/diameter/test/diameter_config_SUITE.erl index 77f7aace1b..bbdf672291 100644 --- a/lib/diameter/test/diameter_config_SUITE.erl +++ b/lib/diameter/test/diameter_config_SUITE.erl @@ -85,6 +85,12 @@ {string_decode, [[true], [false]], [[0], [x]]}, + {incoming_maxlen, + [[0], [65536], [16#FFFFFF]], + [[-1], [1 bsl 24], [infinity], [false]]}, + {spawn_opt, + [[[]], [[monitor, link]]], + [[false]]}, {invalid_option, %% invalid service options are rejected [], [[x], @@ -186,6 +192,9 @@ {private, [[x]], []}, + {spawn_opt, + [[[]], [[monitor, link]]], + [[false]]}, {invalid_option, %% invalid transport options are silently ignored [[x], [x,x]], diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 10c58ab6e7..7dd9f39f85 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -59,6 +59,7 @@ send_unexpected_mandatory_decode/1, send_unexpected_mandatory/1, send_long/1, + send_maxlen/1, send_nopeer/1, send_noapp/1, send_discard/1, @@ -182,6 +183,7 @@ {'Acct-Application-Id', [?DIAMETER_APP_ID_ACCOUNTING]}, {restrict_connections, false}, {string_decode, Decode}, + {incoming_maxlen, 1 bsl 21}, {spawn_opt, [{min_heap_size, 5000}]} | [{application, [{dictionary, D}, {module, ?MODULE}, @@ -317,6 +319,7 @@ tc() -> send_unexpected_mandatory_decode, send_unexpected_mandatory, send_long, + send_maxlen, send_nopeer, send_noapp, send_discard, @@ -635,6 +638,12 @@ send_long(Config) -> ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _] = call(Config, Req). +%% Send something longer than the configure incoming_maxlen. +send_maxlen(Config) -> + Req = ['STR', {'Termination-Cause', ?LOGOUT}, + {'User-Name', [lists:duplicate(1 bsl 21, $X)]}], + {timeout, _} = call(Config, Req). + %% Send something for which pick_peer finds no suitable peer. send_nopeer(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl index 792b6c4a73..f501a4485b 100644 --- a/lib/mnesia/src/mnesia.erl +++ b/lib/mnesia/src/mnesia.erl @@ -145,7 +145,7 @@ %% Local function in order to avoid external function call val(Var) -> case ?catch_val(Var) of - {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); + {'EXIT', _} -> mnesia_lib:other_val(Var); Value -> Value end. @@ -807,7 +807,7 @@ next(Tid,Ts,Tab,Key) tid -> lock_table(Tid, Ts, Tab, read), do_fixtable(Tab,Ts), - New = (catch dirty_next(Tab,Key)), + New = ?CATCH(dirty_next(Tab,Key)), stored_keys(Tab,New,Key,Ts,next, val({Tab, setorbag})); _Protocol -> @@ -833,7 +833,7 @@ prev(Tid,Ts,Tab,Key) tid -> lock_table(Tid, Ts, Tab, read), do_fixtable(Tab,Ts), - New = (catch dirty_prev(Tab,Key)), + New = ?CATCH(dirty_prev(Tab,Key)), stored_keys(Tab,New,Key,Ts,prev, val({Tab, setorbag})); _Protocol -> @@ -965,7 +965,7 @@ foldl(Fun, Acc, Tab, LockKind) when is_function(Fun) -> foldl(ActivityId, Opaque, Fun, Acc, Tab, LockKind) -> {Type, Prev} = init_iteration(ActivityId, Opaque, Tab, LockKind), - Res = (catch do_foldl(ActivityId, Opaque, Tab, dirty_first(Tab), Fun, Acc, Type, Prev)), + Res = ?CATCH(do_foldl(ActivityId, Opaque, Tab, dirty_first(Tab), Fun, Acc, Type, Prev)), close_iteration(Res, Tab). do_foldl(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) -> @@ -1011,7 +1011,7 @@ foldr(ActivityId, Opaque, Fun, Acc, Tab, LockKind) -> true -> %% Order doesn't matter for set and bag TempPrev %% Keep the order so we can use ordsets:del_element end, - Res = (catch do_foldr(ActivityId, Opaque, Tab, dirty_last(Tab), Fun, Acc, Type, Prev)), + Res = ?CATCH(do_foldr(ActivityId, Opaque, Tab, dirty_last(Tab), Fun, Acc, Type, Prev)), close_iteration(Res, Tab). do_foldr(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) -> @@ -1905,21 +1905,21 @@ any_table_info(Tab, _Item) -> abort({bad_type, Tab}). raw_table_info(Tab, Item) -> - case ?catch_val({Tab, storage_type}) of - ram_copies -> - info_reply(catch ?ets_info(Tab, Item), Tab, Item); - disc_copies -> - info_reply(catch ?ets_info(Tab, Item), Tab, Item); - disc_only_copies -> - info_reply(catch dets:info(Tab, Item), Tab, Item); - unknown -> - bad_info_reply(Tab, Item); - {'EXIT', _} -> + try + case ?ets_lookup_element(mnesia_gvar, {Tab, storage_type}, 2) of + ram_copies -> + info_reply(?ets_info(Tab, Item), Tab, Item); + disc_copies -> + info_reply(?ets_info(Tab, Item), Tab, Item); + disc_only_copies -> + info_reply(dets:info(Tab, Item), Tab, Item); + unknown -> + bad_info_reply(Tab, Item) + end + catch error:_ -> bad_info_reply(Tab, Item) end. -info_reply({'EXIT', _Reason}, Tab, Item) -> - bad_info_reply(Tab, Item); info_reply({error, _Reason}, Tab, Item) -> bad_info_reply(Tab, Item); info_reply(Val, _Tab, _Item) -> @@ -2063,9 +2063,8 @@ storage_count(T, {U, R, D, DO}) -> end. system_info(Item) -> - case catch system_info2(Item) of - {'EXIT',Error} -> abort(Error); - Other -> Other + try system_info2(Item) + catch _:Error -> abort(Error) end. system_info2(all) -> @@ -2381,11 +2380,10 @@ del_table_index(Tab, Ix) -> mnesia_schema:del_table_index(Tab, Ix). transform_table(Tab, Fun, NewA) -> - case catch val({Tab, record_name}) of - {'EXIT', Reason} -> - mnesia:abort(Reason); - OldRN -> - mnesia_schema:transform_table(Tab, Fun, NewA, OldRN) + try val({Tab, record_name}) of + OldRN -> mnesia_schema:transform_table(Tab, Fun, NewA, OldRN) + catch exit:Reason -> + mnesia:abort(Reason) end. transform_table(Tab, Fun, NewA, NewRN) -> @@ -2796,7 +2794,7 @@ pre_qlc(Opts, Tab) -> end. post_qlc(Tab) -> - case catch get(mnesia_activity_state) of + case get(mnesia_activity_state) of {_,#tid{},_} -> ok; _ -> case ?catch_val({Tab, setorbag}) of diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl index 9a58ea581a..86b6fd908f 100644 --- a/lib/mnesia/src/mnesia.hrl +++ b/lib/mnesia/src/mnesia.hrl @@ -39,7 +39,12 @@ -define(ets_delete_table(Tab), ets:delete(Tab)). -define(ets_fixtable(Tab, Bool), ets:fixtable(Tab, Bool)). --define(catch_val(Var), (catch ?ets_lookup_element(mnesia_gvar, Var, 2))). + +-define(SAFE(OP), try (OP) catch error:_ -> ok end). +-define(CATCH(OP), try (OP) catch _:_Reason -> {'EXIT', _Reason} end). + +-define(catch_val(Var), (try ?ets_lookup_element(mnesia_gvar, Var, 2) + catch error:_ -> {'EXIT', {badarg, []}} end)). %% It's important that counter is first, since we compare tid's diff --git a/lib/mnesia/src/mnesia_bup.erl b/lib/mnesia/src/mnesia_bup.erl index 71e610bd63..3fee952d77 100644 --- a/lib/mnesia/src/mnesia_bup.erl +++ b/lib/mnesia/src/mnesia_bup.erl @@ -78,24 +78,21 @@ %% BunchOfRecords will be [] when the iteration is done. iterate(Mod, Fun, Opaque, Acc) -> R = #restore{bup_module = Mod, bup_data = Opaque}, - case catch read_schema_section(R) of - {error, Reason} -> - {error, Reason}; - {R2, {Header, Schema, Rest}} -> - case catch iter(R2, Header, Schema, Fun, Acc, Rest) of - {ok, R3, Res} -> - catch safe_apply(R3, close_read, [R3#restore.bup_data]), - {ok, Res}; - {error, Reason} -> - catch safe_apply(R2, close_read, [R2#restore.bup_data]), - {error, Reason}; - {'EXIT', Pid, Reason} -> - catch safe_apply(R2, close_read, [R2#restore.bup_data]), - {error, {'EXIT', Pid, Reason}}; - {'EXIT', Reason} -> - catch safe_apply(R2, close_read, [R2#restore.bup_data]), - {error, {'EXIT', Reason}} - end + try read_schema_section(R) of + {R2, {Header, Schema, Rest}} -> + try iter(R2, Header, Schema, Fun, Acc, Rest) of + {ok, R3, Res} -> + close_read(R3), + {ok, Res} + catch throw:Err -> + close_read(R2), + Err; + _:Reason -> + close_read(R2), + {error, {Reason, erlang:get_stacktrace()}} + end + catch throw:{error,_} = Err -> + Err end. iter(R, Header, Schema, Fun, Acc, []) -> @@ -116,7 +113,7 @@ safe_apply(R, write, [_, Items]) when Items =:= [] -> safe_apply(R, What, Args) -> Abort = fun(Re) -> abort_restore(R, What, Args, Re) end, Mod = R#restore.bup_module, - case catch apply(Mod, What, Args) of + try apply(Mod, What, Args) of {ok, Opaque, Items} when What =:= read -> {R#restore{bup_data = Opaque}, Items}; {ok, Opaque} when What =/= read-> @@ -125,16 +122,19 @@ safe_apply(R, What, Args) -> Abort(Re); Re -> Abort(Re) + catch _:Re -> + Abort(Re) end. -abort_restore(R, What, Args, Reason) -> - Mod = R#restore.bup_module, - Opaque = R#restore.bup_data, +abort_restore(R = #restore{bup_module=Mod}, What, Args, Reason) -> dbg_out("Restore aborted. ~p:~p~p -> ~p~n", [Mod, What, Args, Reason]), - catch apply(Mod, close_read, [Opaque]), + close_read(R), throw({error, Reason}). +close_read(#restore{bup_module=Mod, bup_data=Opaque}) -> + ?SAFE(Mod:close_read(Opaque)). + fallback_to_schema() -> Fname = fallback_bup(), fallback_to_schema(Fname). @@ -145,40 +145,30 @@ fallback_to_schema(Fname) -> {error, Reason} -> {error, Reason}; Schema -> - case catch lookup_schema(schema, Schema) of - {error, _} -> - {error, "No schema in fallback"}; - List -> - {ok, fallback, List} + try lookup_schema(schema, Schema) of + List -> {ok, fallback, List} + catch throw:_ -> + {error, "No schema in fallback"} end end. %% Opens Opaque reads schema and then close read_schema(Mod, Opaque) -> R = #restore{bup_module = Mod, bup_data = Opaque}, - case catch read_schema_section(R) of - {error, Reason} -> - {error, Reason}; - {R2, {_Header, Schema, _}} -> - catch safe_apply(R2, close_read, [R2#restore.bup_data]), - Schema + try read_schema_section(R) of + {_, {_Header, Schema, _}} -> Schema + catch throw:{error,_} = Error -> + Error + after close_read(R) end. %% Open backup media and extract schema %% rewind backup media and leave it open %% Returns {R, {Header, Schema}} read_schema_section(R) -> - case catch do_read_schema_section(R) of - {'EXIT', Reason} -> - catch safe_apply(R, close_read, [R#restore.bup_data]), - {error, {'EXIT', Reason}}; - {error, Reason} -> - catch safe_apply(R, close_read, [R#restore.bup_data]), - {error, Reason}; - {R2, {H, Schema, Rest}} -> - Schema2 = convert_schema(H#log_header.log_version, Schema), - {R2, {H, Schema2, Rest}} - end. + {R2, {H, Schema, Rest}} = do_read_schema_section(R), + Schema2 = convert_schema(H#log_header.log_version, Schema), + {R2, {H, Schema2, Rest}}. do_read_schema_section(R) -> R2 = safe_apply(R, open_read, [R#restore.bup_data]), @@ -201,7 +191,7 @@ do_read_schema_section(R, {ok, B, _C, Rest}, Acc) -> {R, {B, Acc, Rest}}; do_read_schema_section(_R, {error, Reason}, _Acc) -> - {error, Reason}. + throw({error, Reason}). verify_header([H | RawSchema]) when is_record(H, log_header) -> Current = mnesia_log:backup_log_header(), @@ -218,7 +208,7 @@ verify_header([H | RawSchema]) when is_record(H, log_header) -> {error, {"Bad kind of header. Cannot be used as backup.", H}} end; verify_header(RawSchema) -> - {error, {"Missing header. Cannot be used as backup.", catch hd(RawSchema)}}. + {error, {"Missing header. Cannot be used as backup.", ?CATCH(hd(RawSchema))}}. refresh_cookie(Schema, NewCookie) -> case lists:keysearch(schema, 2, Schema) of @@ -345,7 +335,7 @@ create_schema(Ns, ok) -> Str = mk_str(), File = mnesia_lib:dir(Str), file:delete(File), - case catch make_initial_backup(Ns, File, Mod) of + try make_initial_backup(Ns, File, Mod) of {ok, _Res} -> case do_install_fallback(File, Mod) of ok -> @@ -353,8 +343,8 @@ create_schema(Ns, ok) -> ok; {error, Reason} -> {error, Reason} - end; - {error, Reason} -> + end + catch throw:{error, Reason} -> {error, Reason} end end @@ -384,10 +374,11 @@ make_initial_backup(Ns, Opaque, Mod) -> do_apply(_, write, [_, Items], Opaque) when Items =:= [] -> Opaque; do_apply(Mod, What, Args, _Opaque) -> - case catch apply(Mod, What, Args) of + try apply(Mod, What, Args) of {ok, Opaque2} -> Opaque2; - {error, Reason} -> throw({error, Reason}); - {'EXIT', Reason} -> throw({error, {'EXIT', Reason}}) + {error, Reason} -> throw({error, Reason}) + catch _:Reason -> + throw({error, {'EXIT', Reason}}) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -425,11 +416,11 @@ do_install_fallback(_Opaque, Args) -> {error, {badarg, Args}}. check_fallback_args([Arg | Tail], FA) -> - case catch check_fallback_arg_type(Arg, FA) of - {'EXIT', _Reason} -> - {error, {badarg, Arg}}; + try check_fallback_arg_type(Arg, FA) of FA2 -> check_fallback_args(Tail, FA2) + catch error:_ -> + {error, {badarg, Arg}} end; check_fallback_args([], FA) -> {ok, FA}. @@ -484,7 +475,7 @@ install_fallback_master(ClientPid, FA) -> State = {start, FA}, Opaque = FA#fallback_args.opaque, Mod = FA#fallback_args.module, - Res = (catch iterate(Mod, fun restore_recs/4, Opaque, State)), + Res = iterate(Mod, fun restore_recs/4, Opaque, State), unlink(ClientPid), ClientPid ! {self(), Res}, exit(shutdown). @@ -496,9 +487,7 @@ restore_recs(Recs, Header, Schema, {start, FA}) -> %% No records in backup Schema2 = convert_schema(Header#log_header.log_version, Schema), CreateList = lookup_schema(schema, Schema2), - case catch mnesia_schema:list2cs(CreateList) of - {'EXIT', Reason} -> - throw({error, {"Bad schema in restore_recs", Reason}}); + try mnesia_schema:list2cs(CreateList) of Cs -> Ns = get_fallback_nodes(FA, Cs#cstruct.disc_copies), global:set_lock({{mnesia_table_lock, schema}, self()}, Ns, infinity), @@ -508,6 +497,8 @@ restore_recs(Recs, Header, Schema, {start, FA}) -> Res = restore_recs(Recs, Header, Schema2, Pids), global:del_lock({{mnesia_table_lock, schema}, self()}, Ns), Res + catch _:Reason -> + throw({error, {"Bad schema in restore_recs", Reason}}) end; restore_recs([], _Header, _Schema, Pids) -> @@ -579,45 +570,46 @@ fallback_tmp_name() -> "FALLBACK.TMP". fallback_receiver(Master, FA) -> process_flag(trap_exit, true), - case catch register(mnesia_fallback, self()) of - {'EXIT', _} -> - Reason = {already_exists, node()}, - local_fallback_error(Master, Reason); - true -> - FA2 = check_fallback_dir(Master, FA), - Bup = FA2#fallback_args.fallback_bup, - case mnesia_lib:exists(Bup) of - true -> - Reason2 = {already_exists, node()}, - local_fallback_error(Master, Reason2); - false -> - Mod = mnesia_backup, - Tmp = FA2#fallback_args.fallback_tmp, - R = #restore{mode = replace, - bup_module = Mod, - bup_data = Tmp}, - file:delete(Tmp), - case catch fallback_receiver_loop(Master, R, FA2, schema) of - {error, Reason} -> - local_fallback_error(Master, Reason); - Other -> - exit(Other) - end - end - end. + Res = try + register(mnesia_fallback, self()), + FA2 = check_fallback_dir(FA), + Bup = FA2#fallback_args.fallback_bup, + false = mnesia_lib:exists(Bup), + Mod = mnesia_backup, + Tmp = FA2#fallback_args.fallback_tmp, + R = #restore{mode = replace, + bup_module = Mod, + bup_data = Tmp}, + file:delete(Tmp), + fallback_receiver_loop(Master, R, FA2, schema) + catch + error:_ -> + Reason = {already_exists, node()}, + local_fallback_error(Master, Reason); + throw:{error, Reason} -> + local_fallback_error(Master, Reason) + end, + exit(Res). local_fallback_error(Master, Reason) -> Master ! {self(), {error, Reason}}, unlink(Master), exit(Reason). + check_fallback_dir(Master, FA) -> + try check_fallback_dir(FA) + catch throw:{error,Reason} -> + local_fallback_error(Master, Reason) + end. + +check_fallback_dir(FA) -> case mnesia:system_info(schema_location) of ram -> Reason = {has_no_disc, node()}, - local_fallback_error(Master, Reason); + throw({error, Reason}); _ -> - Dir = check_fallback_dir_arg(Master, FA), + Dir = check_fallback_dir_arg(FA), Bup = filename:join([Dir, fallback_name()]), Tmp = filename:join([Dir, fallback_tmp_name()]), FA#fallback_args{fallback_bup = Bup, @@ -625,22 +617,20 @@ check_fallback_dir(Master, FA) -> mnesia_dir = Dir} end. -check_fallback_dir_arg(Master, FA) -> +check_fallback_dir_arg(FA) -> case FA#fallback_args.use_default_dir of true -> mnesia_lib:dir(); false when FA#fallback_args.scope =:= local -> Dir = FA#fallback_args.mnesia_dir, - case catch mnesia_monitor:do_check_type(dir, Dir) of - {'EXIT', _R} -> + try mnesia_monitor:do_check_type(dir, Dir) + catch _:_ -> Reason = {badarg, {dir, Dir}, node()}, - local_fallback_error(Master, Reason); - AbsDir-> - AbsDir - end; + throw({error, Reason}) + end; false when FA#fallback_args.scope =:= global -> Reason = {combine_error, global, dir, node()}, - local_fallback_error(Master, Reason) + throw({error, Reason}) end. fallback_receiver_loop(Master, R, FA, State) -> @@ -666,7 +656,7 @@ fallback_receiver_loop(Master, R, FA, State) -> Bup = FA#fallback_args.fallback_bup, Tmp = FA#fallback_args.fallback_tmp, throw_bad_res(ok, file:rename(Tmp, Bup)), - catch mnesia_lib:set(active_fallback, true), + ?SAFE(mnesia_lib:set(active_fallback, true)), ?eval_debug_fun({?MODULE, fallback_receiver_loop, post_swap}, []), Master ! {self(), ok}, fallback_receiver_loop(Master, R, FA, stop); @@ -697,7 +687,7 @@ throw_bad_res(_Expected, Actual) -> throw({error, Actual}). tm_fallback_start(IgnoreFallback) -> mnesia_schema:lock_schema(), Res = do_fallback_start(fallback_exists(), IgnoreFallback), - mnesia_schema: unlock_schema(), + mnesia_schema:unlock_schema(), case Res of ok -> ok; {error, Reason} -> exit(Reason) @@ -715,9 +705,9 @@ do_fallback_start(true, false) -> BupFile = fallback_bup(), Mod = mnesia_backup, LocalTabs = ?ets_new_table(mnesia_local_tables, [set, public, {keypos, 2}]), - case catch iterate(Mod, fun restore_tables/4, BupFile, {start, LocalTabs}) of + case iterate(Mod, fun restore_tables/4, BupFile, {start, LocalTabs}) of {ok, _Res} -> - catch dets:close(schema), + ?SAFE(dets:close(schema)), TmpSchema = mnesia_lib:tab2tmp(schema), DatSchema = mnesia_lib:tab2dat(schema), AllLT = ?ets_match_object(LocalTabs, '_'), @@ -733,8 +723,6 @@ do_fallback_start(true, false) -> {error, {"Cannot start from fallback. Rename error.", Reason}} end; {error, Reason} -> - {error, {"Cannot start from fallback", Reason}}; - {'EXIT', Reason} -> {error, {"Cannot start from fallback", Reason}} end. @@ -996,10 +984,10 @@ uninstall_fallback_master(ClientPid, FA) -> case fallback_to_schema(Bup) of {ok, fallback, List} -> Cs = mnesia_schema:list2cs(List), - case catch get_fallback_nodes(FA, Cs#cstruct.disc_copies) of + try get_fallback_nodes(FA, Cs#cstruct.disc_copies) of Ns when is_list(Ns) -> - do_uninstall(ClientPid, Ns, FA); - {error, Reason} -> + do_uninstall(ClientPid, Ns, FA) + catch throw:{error, Reason} -> local_fallback_error(ClientPid, Reason) end; {error, Reason} -> @@ -1042,13 +1030,13 @@ local_uninstall_fallback(Master, FA) -> %% Don't trap exit register(mnesia_fallback, self()), % May exit - FA2 = check_fallback_dir(Master, FA), % May exit + FA2 = check_fallback_dir(Master, FA), % May exit Master ! {self(), started}, receive {Master, do_uninstall} -> ?eval_debug_fun({?MODULE, uninstall_fallback2, pre_delete}, []), - catch mnesia_lib:set(active_fallback, false), + ?SAFE(mnesia_lib:set(active_fallback, false)), Tmp = FA2#fallback_args.fallback_tmp, Bup = FA2#fallback_args.fallback_bup, file:delete(Tmp), @@ -1071,10 +1059,8 @@ rec_uninstall(ClientPid, [Pid | Pids], AccRes) -> {Pid, BadRes} -> rec_uninstall(ClientPid, Pids, BadRes) end; -rec_uninstall(ClientPid, [], Res) -> - ClientPid ! {self(), Res}, - unlink(ClientPid), - exit(normal). +rec_uninstall(_, [], Res) -> + Res. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Backup traversal @@ -1125,12 +1111,11 @@ do_traverse_backup(ClientPid, Source, SourceMod, Target, TargetMod, Fun, Acc) -> Iter = if TargetMod =/= read_only -> - case catch do_apply(TargetMod, open_write, [Target], Target) of - {error, Error} -> + try do_apply(TargetMod, open_write, [Target], Target) + catch throw:{error, Error} -> unlink(ClientPid), ClientPid ! {iter_done, self(), {error, Error}}, - exit(Error); - Else -> Else + exit(Error) end; true -> ignore @@ -1139,16 +1124,16 @@ do_traverse_backup(ClientPid, Source, SourceMod, Target, TargetMod, Fun, Acc) -> Res = case iterate(SourceMod, fun trav_apply/4, Source, A) of {ok, {iter, _, Acc2, _, Iter2}} when TargetMod =/= read_only -> - case catch do_apply(TargetMod, commit_write, [Iter2], Iter2) of - {error, Reason} -> - {error, Reason}; - _ -> - {ok, Acc2} + try + do_apply(TargetMod, commit_write, [Iter2], Iter2), + {ok, Acc2} + catch throw:{error, Reason} -> + {error, Reason} end; {ok, {iter, _, Acc2, _, _}} -> {ok, Acc2}; {error, Reason} when TargetMod =/= read_only-> - catch do_apply(TargetMod, abort_write, [Iter], Iter), + ?CATCH(do_apply(TargetMod, abort_write, [Iter], Iter)), {error, {"Backup traversal failed", Reason}}; {error, Reason} -> {error, {"Backup traversal failed", Reason}} diff --git a/lib/mnesia/src/mnesia_checkpoint.erl b/lib/mnesia/src/mnesia_checkpoint.erl index 442e68a13d..0a3ea8d769 100644 --- a/lib/mnesia/src/mnesia_checkpoint.erl +++ b/lib/mnesia/src/mnesia_checkpoint.erl @@ -128,7 +128,7 @@ tm_enter_pending([], Pending) -> Pending; tm_enter_pending([Tab | Tabs], Pending) -> %% io:format("Add ~p ~p ~p~n",[Tab, Pending, hd(tl(element(2, process_info(self(), current_stacktrace))))]), - catch ?ets_insert(Tab, Pending), + ?SAFE(?ets_insert(Tab, Pending)), tm_enter_pending(Tabs, Pending). tm_exit_pending(Tid) -> @@ -427,22 +427,22 @@ check_tables(Cp) -> arrange_retainers(Cp, Overriders, AllTabs) -> R = #retainer{cp_name = Cp#checkpoint_args.name}, - case catch [R#retainer{tab_name = Tab, - writers = select_writers(Cp, Tab)} - || Tab <- AllTabs] of - {'EXIT', Reason} -> - {error, Reason}; + try [R#retainer{tab_name = Tab, + writers = select_writers(Cp, Tab)} + || Tab <- AllTabs] of Retainers -> {ok, Cp#checkpoint_args{ram_overrides_dump = Overriders, - retainers = Retainers, - nodes = writers(Retainers)}} + retainers = Retainers, + nodes = writers(Retainers)}} + catch throw:Reason -> + {error, Reason} end. select_writers(Cp, Tab) -> case filter_remote(Cp, val({Tab, active_replicas})) of [] -> - exit({"Cannot prepare checkpoint (replica not available)", - [Tab, Cp#checkpoint_args.name]}); + throw({"Cannot prepare checkpoint (replica not available)", + [Tab, Cp#checkpoint_args.name]}); Writers -> This = node(), case {lists:member(Tab, Cp#checkpoint_args.max), @@ -492,12 +492,12 @@ check_prep([], Name, Nodes, IgnoreNew) -> collect_pending(Name, Nodes, IgnoreNew) -> case rpc:multicall(Nodes, ?MODULE, call, [Name, collect_pending]) of {Replies, []} -> - case catch ?ets_new_table(mnesia_union, [bag]) of - {'EXIT', Reason} -> %% system limit + try + UnionTab = ?ets_new_table(mnesia_union, [bag]), + compute_union(Replies, Nodes, Name, UnionTab, IgnoreNew) + catch error:Reason -> %% system limit Msg = "Cannot create an ets table pending union", - {error, {system_limit, Msg, Reason}}; - UnionTab -> - compute_union(Replies, Nodes, Name, UnionTab, IgnoreNew) + {error, {system_limit, Msg, Reason}} end; {_, BadNodes} -> deactivate(Nodes, Name), @@ -1170,7 +1170,7 @@ iterate(Name, Tab, Fun, Acc, Source, Val) -> {error, Reason}; {ok, Iter, Pid} -> link(Pid), % We don't want any pending fixtable's - Res = (catch iter(Fun, Acc, Iter)), + Res = ?CATCH(iter(Fun, Acc, Iter)), unlink(Pid), call(Name, {iter_end, Iter}), case Res of @@ -1246,7 +1246,7 @@ system_code_change(Cp, _Module, _OldVsn, _Extra) -> val(Var) -> case ?catch_val(Var) of - {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_); - _VaLuE_ -> _VaLuE_ + {'EXIT', _} -> mnesia_lib:other_val(Var); + _VaLuE_ -> _VaLuE_ end. diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index aa72de7594..b9d3779e9a 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -186,7 +186,7 @@ max_loaders() -> val(Var) -> case ?catch_val(Var) of - {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); + {'EXIT', _} -> mnesia_lib:other_val(Var); Value -> Value end. @@ -241,9 +241,7 @@ do_wait_for_tables(Tabs, Timeout) -> end. reply_wait(Tabs) -> - case catch mnesia_lib:active_tables() of - {'EXIT', _} -> - {error, {node_not_running, node()}}; + try mnesia_lib:active_tables() of Active when is_list(Active) -> case Tabs -- Active of [] -> @@ -251,6 +249,7 @@ reply_wait(Tabs) -> BadTabs -> {timeout, BadTabs} end + catch exit:_ -> {error, {node_not_running, node()}} end. wait_for_tables_init(From, Tabs) -> @@ -261,13 +260,12 @@ wait_for_tables_init(From, Tabs) -> exit(normal). wait_for_init(From, Tabs, Init) -> - case catch link(Init) of - {'EXIT', _} -> - %% Mnesia is not started - {error, {node_not_running, node()}}; + try link(Init) of true when is_pid(Init) -> cast({sync_tabs, Tabs, self()}), rec_tabs(Tabs, Tabs, From, Init) + catch error:_ -> %% Mnesia is not started + {error, {node_not_running, node()}} end. sync_reply(Waiter, Tab) -> @@ -343,7 +341,7 @@ get_network_copy(Tab, Cs) -> %% might be solved by using monitor in subscr instead. process_flag(trap_exit, true), Load = load_table_fun(Work), - Res = (catch Load()), + Res = ?CATCH(Load()), process_flag(trap_exit, false), call({del_other, self()}), case Res of @@ -592,11 +590,8 @@ call(Msg) -> end. remote_call(Node, Func, Args) -> - case catch gen_server:call({?MODULE, Node}, {Func, Args, self()}, infinity) of - {'EXIT', Error} -> - {error, Error}; - Else -> - Else + try gen_server:call({?MODULE, Node}, {Func, Args, self()}, infinity) + catch exit:Error -> {error, Error} end. multicall(Nodes, Msg) -> @@ -677,7 +672,7 @@ handle_call(block_controller, From, State) -> noreply(State2); handle_call({update,Fun}, From, State) -> - Res = (catch Fun()), + Res = ?CATCH(Fun()), reply(From, Res), noreply(State); @@ -1256,7 +1251,7 @@ handle_info(#sender_done{worker_pid=Pid, worker_res=Res}, State) -> end; handle_info({'EXIT', Pid, R}, State) when Pid == State#state.supervisor -> - catch set(mnesia_status, stopping), + ?SAFE(set(mnesia_status, stopping)), case State#state.dumper_pid of undefined -> dbg_out("~p was ~p~n", [?SERVER_NAME, R]), @@ -1480,9 +1475,9 @@ orphan_tables([], _, _, LocalOrphans, RemoteMasters) -> node_has_tabs([Tab | Tabs], Node, State) when Node /= node() -> State2 = - case catch update_whereabouts(Tab, Node, State) of - State1 = #state{} -> State1; - {'EXIT', R} -> %% Tab was just deleted? + try update_whereabouts(Tab, Node, State) of + State1 = #state{} -> State1 + catch exit:R -> %% Tab was just deleted? case ?catch_val({Tab, cstruct}) of {'EXIT', _} -> State; % yes _ -> erlang:error(R) @@ -1768,22 +1763,17 @@ change_table_majority(Cs) -> update_where_to_wlock(Tab) -> WNodes = val({Tab, where_to_write}), - Majority = case catch val({Tab, majority}) of - true -> true; - _ -> false - end, + Majority = ?catch_val({Tab, majority}) == true, set({Tab, where_to_wlock}, {WNodes, Majority}). %% node To now has tab loaded, but this must be undone %% This code is rpc:call'ed from the tab_copier process %% when it has *not* released it's table lock unannounce_add_table_copy(Tab, To) -> - catch del_active_replica(Tab, To), - case catch val({Tab , where_to_read}) of - To -> - mnesia_lib:set_remote_where_to_read(Tab); - _ -> - ignore + ?SAFE(del_active_replica(Tab, To)), + try To = val({Tab , where_to_read}), + mnesia_lib:set_remote_where_to_read(Tab) + catch _:_ -> ignore end. user_sync_tab(Tab) -> diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl index 509b765dee..693f20dbc2 100644 --- a/lib/mnesia/src/mnesia_dumper.erl +++ b/lib/mnesia/src/mnesia_dumper.erl @@ -137,7 +137,7 @@ perform_dump(InitBy, Regulator) -> U = mnesia_monitor:get_env(dump_log_update_in_place), Cont = mnesia_log:init_log_dump(), mnesia_recover:sync(), - case catch do_perform_dump(Cont, U, InitBy, Regulator, undefined) of + try do_perform_dump(Cont, U, InitBy, Regulator, undefined) of ok -> ?eval_debug_fun({?MODULE, post_dump}, [InitBy]), case mnesia_monitor:use_dir() of @@ -148,17 +148,15 @@ perform_dump(InitBy, Regulator) -> end, mnesia_recover:allow_garb(), %% And now to the crucial point... - mnesia_log:confirm_log_dump(Diff); - {error, Reason} -> - {error, Reason}; - {'EXIT', {Desc, Reason}} -> + mnesia_log:confirm_log_dump(Diff) + catch exit:Reason when Reason =/= fatal -> case mnesia_monitor:get_env(auto_repair) of true -> - mnesia_lib:important(Desc, Reason), + mnesia_lib:important(error, Reason), %% Ignore rest of the log mnesia_log:confirm_log_dump(Diff); false -> - fatal(Desc, Reason) + fatal(error, Reason) end end; {error, Reason} -> @@ -176,24 +174,25 @@ scan_decisions(Fname, InitBy, Regulator) -> mnesia_log:open_log(Name, Header, Fname, Exists, mnesia_monitor:get_env(auto_repair), read_only), Cont = start, - Res = (catch do_perform_dump(Cont, false, InitBy, Regulator, undefined)), - mnesia_log:close_log(Name), - case Res of - ok -> ok; - {'EXIT', Reason} -> {error, Reason} + try + do_perform_dump(Cont, false, InitBy, Regulator, undefined) + catch exit:Reason when Reason =/= fatal -> + {error, Reason} + after mnesia_log:close_log(Name) end end. do_perform_dump(Cont, InPlace, InitBy, Regulator, OldVersion) -> case mnesia_log:chunk_log(Cont) of {C2, Recs} -> - case catch insert_recs(Recs, InPlace, InitBy, Regulator, OldVersion) of - {'EXIT', R} -> - Reason = {"Transaction log dump error: ~p~n", [R]}, - close_files(InPlace, {error, Reason}, InitBy), - exit(Reason); + try insert_recs(Recs, InPlace, InitBy, Regulator, OldVersion) of Version -> 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}]}, + close_files(InPlace, {error, Reason}, InitBy), + exit(Reason) end; eof -> close_files(InPlace, ok, InitBy), @@ -303,17 +302,16 @@ perform_update(Tid, SchemaOps, _DumperMode, _UseDir) -> InitBy = fast_schema_update, InPlace = mnesia_monitor:get_env(dump_log_update_in_place), - ?eval_debug_fun({?MODULE, dump_schema_op}, [InitBy]), - case catch insert_ops(Tid, schema_ops, SchemaOps, InPlace, InitBy, - mnesia_log:version()) of - {'EXIT', Reason} -> - Error = {error, {"Schema update error", Reason}}, + try insert_ops(Tid, schema_ops, SchemaOps, InPlace, InitBy, + mnesia_log:version()), + ?eval_debug_fun({?MODULE, post_dump}, [InitBy]), + close_files(InPlace, ok, InitBy), + ok + catch _:Reason when Reason =/= fatal -> + ST = erlang:get_stacktrace(), + Error = {error, {"Schema update error", {Reason, ST}}}, close_files(InPlace, Error, InitBy), - fatal("Schema update error ~p ~p", [Reason, SchemaOps]); - _ -> - ?eval_debug_fun({?MODULE, post_dump}, [InitBy]), - close_files(InPlace, ok, InitBy), - ok + fatal("Schema update error ~p ~p", [{Reason,ST}, SchemaOps]) end. insert_ops(_Tid, _Storage, [], _InPlace, _InitBy, _) -> ok; @@ -362,13 +360,11 @@ dets_insert(Op,Tab,Key,Val) -> case dets_incr_counter(Tab,Key) of true -> {RecName, Incr} = Val, - case catch dets:update_counter(Tab, Key, Incr) of - CounterVal when is_integer(CounterVal) -> - ok; - _ when Incr < 0 -> + try _ = dets:update_counter(Tab, Key, Incr) + catch error:_ when Incr < 0 -> Zero = {RecName, Key, 0}, ok = dets:insert(Tab, Zero); - _ -> + error:_ -> Init = {RecName, Key, Incr}, ok = dets:insert(Tab, Init) end; @@ -786,7 +782,7 @@ insert_op(Tid, _, {op, clear_table, TabDef}, InPlace, InitBy) -> end, %% Need to catch this, it crashes on ram_copies if %% the op comes before table is loaded at startup. - catch insert(Tid, Storage, Tab, '_', Oid, clear_table, InPlace, InitBy) + ?CATCH(insert(Tid, Storage, Tab, '_', Oid, clear_table, InPlace, InitBy)) end; insert_op(Tid, _, {op, merge_schema, TabDef}, InPlace, InitBy) -> @@ -1060,14 +1056,13 @@ prepare_open(Tab, UpdateInPlace) -> Dat; false -> Tmp = mnesia_lib:tab2tmp(Tab), - case catch mnesia_lib:copy_file(Dat, Tmp) of - ok -> - Tmp; - Error -> + try ok = mnesia_lib:copy_file(Dat, Tmp) + catch error:Error -> fatal("Cannot copy dets file ~p to ~p: ~p~n", [Dat, Tmp, Error]) - end - end. + end, + Tmp + end. del_opened_tab(Tab) -> erase({?MODULE, Tab}). @@ -1189,18 +1184,16 @@ raw_named_dump_table(Tab, Ftype) -> Storage = ram_copies, mnesia_lib:db_fixtable(Storage, Tab, true), - case catch raw_dump_table(TabRef, Tab) of - {'EXIT', Reason} -> - mnesia_lib:db_fixtable(Storage, Tab, false), - mnesia_lib:dets_sync_close(Tab), - file:delete(TmpFname), - mnesia_lib:unlock_table(Tab), - exit({"Dump of table to disc failed", Reason}); - ok -> - mnesia_lib:db_fixtable(Storage, Tab, false), - mnesia_lib:dets_sync_close(Tab), - mnesia_lib:unlock_table(Tab), - ok = file:rename(TmpFname, Fname) + try + ok = raw_dump_table(TabRef, Tab), + ok = file:rename(TmpFname, Fname) + catch _:Reason -> + ?SAFE(file:delete(TmpFname)), + exit({"Dump of table to disc failed", Reason}) + after + mnesia_lib:db_fixtable(Storage, Tab, false), + mnesia_lib:dets_sync_close(Tab), + mnesia_lib:unlock_table(Tab) end; {error, Reason} -> mnesia_lib:unlock_table(Tab), @@ -1266,6 +1259,6 @@ regulate(RegulatorPid) -> val(Var) -> case ?catch_val(Var) of - {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); + {'EXIT', _} -> mnesia_lib:other_val(Var); Value -> Value end. diff --git a/lib/mnesia/src/mnesia_frag.erl b/lib/mnesia/src/mnesia_frag.erl index 66fc20913c..6036ac4e8f 100644 --- a/lib/mnesia/src/mnesia_frag.erl +++ b/lib/mnesia/src/mnesia_frag.erl @@ -406,10 +406,11 @@ verify_numbers(FH,MatchSpec) -> VerifyFun = fun(F) when is_integer(F), F >= 1, F =< N -> false; (_F) -> true end, - case catch lists:filter(VerifyFun, FragNumbers) of - [] -> - FragNumbers; - BadFrags -> + try + Frags = lists:filter(VerifyFun, FragNumbers), + Frags == [] orelse error(Frags), + FragNumbers + catch error:BadFrags -> mnesia:abort({"match_spec_to_frag_numbers: Fragment numbers out of range", BadFrags, {range, 1, N}}) end. @@ -437,7 +438,7 @@ remote_select(ReplyTo, Ref, NameNodes, MatchSpec) -> do_remote_select(ReplyTo, Ref, [{Name, Node} | NameNodes], MatchSpec) -> if Node == node() -> - Res = (catch {ok, mnesia:dirty_select(Name, MatchSpec)}), + Res = ?CATCH({ok, mnesia:dirty_select(Name, MatchSpec)}), ReplyTo ! {remote_select, Ref, Node, Res}, do_remote_select(ReplyTo, Ref, NameNodes, MatchSpec); true -> @@ -886,17 +887,19 @@ adjust_before_split(FH) -> HashMod:add_frag(HashState) end, N = FH#frag_state.n_fragments + 1, - FromFrags2 = (catch lists:sort(FromFrags)), - UnionFrags = (catch lists:merge(FromFrags2, lists:sort(AdditionalWriteFrags))), VerifyFun = fun(F) when is_integer(F), F >= 1, F =< N -> false; (_F) -> true end, - case catch lists:filter(VerifyFun, UnionFrags) of - [] -> - FH2 = FH#frag_state{n_fragments = N, - hash_state = HashState2}, - {FH2, FromFrags2, UnionFrags}; - BadFrags -> + try + FromFrags2 = lists:sort(FromFrags), + UnionFrags = lists:merge(FromFrags2, lists:sort(AdditionalWriteFrags)), + + Frags = lists:filter(VerifyFun, UnionFrags), + Frags == [] orelse error(Frags), + FH2 = FH#frag_state{n_fragments = N, + hash_state = HashState2}, + {FH2, FromFrags2, UnionFrags} + catch error:BadFrags -> mnesia:abort({"add_frag: Fragment numbers out of range", BadFrags, {range, 1, N}}) end. @@ -981,22 +984,24 @@ adjust_before_merge(FH) -> HashMod:del_frag(HashState) end, N = FH#frag_state.n_fragments, - FromFrags2 = (catch lists:sort(FromFrags)), - UnionFrags = (catch lists:merge(FromFrags2, lists:sort(AdditionalWriteFrags))), VerifyFun = fun(F) when is_integer(F), F >= 1, F =< N -> false; (_F) -> true end, - case catch lists:filter(VerifyFun, UnionFrags) of - [] -> - case lists:member(N, FromFrags2) of - true -> - FH2 = FH#frag_state{n_fragments = N - 1, - hash_state = HashState2}, - {FH2, FromFrags2, UnionFrags}; + try + FromFrags2 = lists:sort(FromFrags), + UnionFrags = lists:merge(FromFrags2, lists:sort(AdditionalWriteFrags)), + + Frags = lists:filter(VerifyFun, UnionFrags), + [] == Frags orelse error(Frags), + case lists:member(N, FromFrags2) of + true -> + FH2 = FH#frag_state{n_fragments = N - 1, + hash_state = HashState2}, + {FH2, FromFrags2, UnionFrags}; false -> - mnesia:abort({"del_frag: Last fragment number not included", N}) - end; - BadFrags -> + mnesia:abort({"del_frag: Last fragment number not included", N}) + end + catch error:BadFrags -> mnesia:abort({"del_frag: Fragment numbers out of range", BadFrags, {range, 1, N}}) end. @@ -1141,8 +1146,8 @@ remove_node(Node, Cs) -> val(Var) -> case ?catch_val(Var) of - {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); - Value -> Value + {'EXIT', _} -> mnesia_lib:other_val(Var); + Value -> Value end. set_frag_hash(Tab, Props) -> diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl index 87cb58dae1..6a7c964fce 100644 --- a/lib/mnesia/src/mnesia_index.erl +++ b/lib/mnesia/src/mnesia_index.erl @@ -45,21 +45,11 @@ del_transient/3, del_index_table/3]). --import(mnesia_lib, [verbose/2]). +-import(mnesia_lib, [val/1, verbose/2]). -include("mnesia.hrl"). -record(index, {setorbag, pos_list}). -val(Var) -> - case ?catch_val(Var) of - {'EXIT', _ReASoN_} -> - case mnesia_lib:other_val(Var) of - error -> mnesia_lib:pr_other(Var, _ReASoN_); - Val -> Val - end; - _VaLuE_ -> _VaLuE_ - end. - %% read an object list throuh its index table %% we assume that table Tab has index on attribute number Pos diff --git a/lib/mnesia/src/mnesia_late_loader.erl b/lib/mnesia/src/mnesia_late_loader.erl index d09de3ca66..9a113c6306 100644 --- a/lib/mnesia/src/mnesia_late_loader.erl +++ b/lib/mnesia/src/mnesia_late_loader.erl @@ -36,17 +36,19 @@ -define(SERVER_NAME, ?MODULE). +-include("mnesia.hrl"). + -record(state, {supervisor}). async_late_disc_load(_, [], _) -> ok; async_late_disc_load(Node, Tabs, Reason) -> Msg = {async_late_disc_load, Tabs, Reason}, - catch ({?SERVER_NAME, Node} ! {self(), Msg}). + ?SAFE({?SERVER_NAME, Node} ! {self(), Msg}). maybe_async_late_disc_load(_, [], _) -> ok; maybe_async_late_disc_load(Node, Tabs, Reason) -> Msg = {maybe_async_late_disc_load, Tabs, Reason}, - catch ({?SERVER_NAME, Node} ! {self(), Msg}). + ?SAFE({?SERVER_NAME, Node} ! {self(), Msg}). start() -> mnesia_monitor:start_proc(?SERVER_NAME, ?MODULE, init, [self()]). diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl index fc41cbedcb..7bd207f816 100644 --- a/lib/mnesia/src/mnesia_lib.erl +++ b/lib/mnesia/src/mnesia_lib.erl @@ -114,9 +114,7 @@ lock_table/1, mkcore/1, not_active_here/1, - other_val/2, other_val/1, - pr_other/2, overload_read/0, overload_read/1, overload_set/2, @@ -380,8 +378,8 @@ search_key(_Key, []) -> val(Var) -> case ?catch_val(Var) of - {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_); - _VaLuE_ -> _VaLuE_ + {'EXIT', _} -> other_val(Var); + _VaLuE_ -> _VaLuE_ end. set(Var, Val) -> @@ -390,13 +388,13 @@ set(Var, Val) -> unset(Var) -> ?ets_delete(mnesia_gvar, Var). -other_val(Var, Other) -> - case other_val(Var) of - error -> pr_other(Var, Other); +other_val(Var) -> + case other_val_1(Var) of + error -> pr_other(Var); Val -> Val end. -other_val(Var) -> +other_val_1(Var) -> case Var of {_, where_to_read} -> nowhere; {_, where_to_write} -> []; @@ -404,21 +402,16 @@ other_val(Var) -> _ -> error end. -pr_other(Var, Other) -> - Why = +pr_other(Var) -> + Why = case is_running() of no -> {node_not_running, node()}; _ -> {no_exists, Var} end, - verbose("~p (~p) val(mnesia_gvar, ~w) -> ~p ~p ~n", + verbose("~p (~p) val(mnesia_gvar, ~w) -> ~p ~n", [self(), process_info(self(), registered_name), - Var, Other, Why]), - case Other of - {badarg, [{ets, lookup_element, _, _}|_]} -> - exit(Why); - _ -> - erlang:error(Why) - end. + Var, Why]), + exit(Why). %% Some functions for list valued variables add(Var, Val) -> @@ -905,7 +898,7 @@ dirty_rpc_error_tag(Reason) -> end. fatal(Format, Args) -> - catch set(mnesia_status, stopping), + ?SAFE(catch set(mnesia_status, stopping)), Core = mkcore({crashinfo, {Format, Args}}), report_fatal(Format, Args, Core), timer:sleep(10000), % Enough to write the core dump to disc? @@ -917,7 +910,7 @@ report_fatal(Format, Args) -> report_fatal(Format, Args, Core) -> report_system_event({mnesia_fatal, Format, Args, Core}), - catch exit(whereis(mnesia_monitor), fatal). + ?SAFE(exit(whereis(mnesia_monitor), fatal)). %% We sleep longer and longer the more we try %% Made some testing and came up with the following constants @@ -1014,7 +1007,7 @@ dbg_out(Format, Args) -> %% Keep the last 10 debug print outs save(DbgInfo) -> - catch save2(DbgInfo). + ?SAFE(save2(DbgInfo)). save2(DbgInfo) -> Key = {'$$$_report', current_pos}, @@ -1090,35 +1083,29 @@ db_match_object(Tab, Pat) -> db_match_object(val({Tab, storage_type}), Tab, Pat). db_match_object(Storage, Tab, Pat) -> db_fixtable(Storage, Tab, true), - Res = catch_match_object(Storage, Tab, Pat), - db_fixtable(Storage, Tab, false), - case Res of - {'EXIT', Reason} -> exit(Reason); - _ -> Res + try + case Storage of + disc_only_copies -> dets:match_object(Tab, Pat); + _ -> ets:match_object(Tab, Pat) + end + after + db_fixtable(Storage, Tab, false) end. -catch_match_object(disc_only_copies, Tab, Pat) -> - catch dets:match_object(Tab, Pat); -catch_match_object(_, Tab, Pat) -> - catch ets:match_object(Tab, Pat). - db_select(Tab, Pat) -> db_select(val({Tab, storage_type}), Tab, Pat). db_select(Storage, Tab, Pat) -> db_fixtable(Storage, Tab, true), - Res = catch_select(Storage, Tab, Pat), - db_fixtable(Storage, Tab, false), - case Res of - {'EXIT', Reason} -> exit(Reason); - _ -> Res + try + case Storage of + disc_only_copies -> dets:select(Tab, Pat); + _ -> ets:select(Tab, Pat) + end + after + db_fixtable(Storage, Tab, false) end. -catch_select(disc_only_copies, Tab, Pat) -> - catch dets:select(Tab, Pat); -catch_select(_, Tab, Pat) -> - catch ets:select(Tab, Pat). - db_select_init(disc_only_copies, Tab, Pat, Limit) -> dets:select(Tab, Pat, Limit); db_select_init(_, Tab, Pat, Limit) -> @@ -1262,7 +1249,7 @@ dets_sync_open(Tab, Args) -> end. dets_sync_close(Tab) -> - catch dets:close(Tab), + ?SAFE(dets:close(Tab)), unlock_table(Tab), ok. @@ -1298,7 +1285,7 @@ readable_indecies(Tab) -> scratch_debug_fun() -> dbg_out("scratch_debug_fun(): ~p~n", [?DEBUG_TAB]), - (catch ?ets_delete_table(?DEBUG_TAB)), + ?SAFE(?ets_delete_table(?DEBUG_TAB)), ?ets_new_table(?DEBUG_TAB, [set, public, named_table, {keypos, 2}]). activate_debug_fun(FunId, Fun, InitialContext, File, Line) -> @@ -1311,43 +1298,45 @@ activate_debug_fun(FunId, Fun, InitialContext, File, Line) -> update_debug_info(Info). update_debug_info(Info) -> - case catch ?ets_insert(?DEBUG_TAB, Info) of - {'EXIT', _} -> + try ?ets_insert(?DEBUG_TAB, Info), + ok + catch error:_ -> scratch_debug_fun(), - ?ets_insert(?DEBUG_TAB, Info); - _ -> - ok + ?ets_insert(?DEBUG_TAB, Info) end, dbg_out("update_debug_info(~p)~n", [Info]), ok. deactivate_debug_fun(FunId, _File, _Line) -> - catch ?ets_delete(?DEBUG_TAB, FunId), + ?SAFE(?ets_delete(?DEBUG_TAB, FunId)), ok. eval_debug_fun(FunId, EvalContext, EvalFile, EvalLine) -> - case catch ?ets_lookup(?DEBUG_TAB, FunId) of - [] -> - ok; - [Info] -> - OldContext = Info#debug_info.context, - dbg_out("~s(~p): ~w " - "activated in ~s(~p)~n " - "eval_debug_fun(~w, ~w)~n", - [filename:basename(EvalFile), EvalLine, Info#debug_info.id, - filename:basename(Info#debug_info.file), Info#debug_info.line, - OldContext, EvalContext]), - Fun = Info#debug_info.function, - NewContext = Fun(OldContext, EvalContext), - - case catch ?ets_lookup(?DEBUG_TAB, FunId) of - [Info] when NewContext /= OldContext -> - NewInfo = Info#debug_info{context = NewContext}, - update_debug_info(NewInfo); - _ -> - ok - end; - {'EXIT', _} -> ok + try + case ?ets_lookup(?DEBUG_TAB, FunId) of + [] -> + ok; + [Info] -> + OldContext = Info#debug_info.context, + dbg_out("~s(~p): ~w " + "activated in ~s(~p)~n " + "eval_debug_fun(~w, ~w)~n", + [filename:basename(EvalFile), EvalLine, Info#debug_info.id, + filename:basename(Info#debug_info.file), Info#debug_info.line, + OldContext, EvalContext]), + Fun = Info#debug_info.function, + NewContext = Fun(OldContext, EvalContext), + + case ?ets_lookup(?DEBUG_TAB, FunId) of + [Info] when NewContext /= OldContext -> + NewInfo = Info#debug_info{context = NewContext}, + update_debug_info(NewInfo); + _ -> + ok + end + end + catch error -> + ok end. -ifdef(debug). diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index cbb3d7e430..65ea743fd3 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2013. All Rights Reserved. +%% Copyright Ericsson AB 1998-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ val(Var) -> case ?catch_val(Var) of - {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); + {'EXIT', _} -> mnesia_lib:other_val(Var); Value -> Value end. @@ -332,7 +332,7 @@ wait_on_load_complete(Pid) -> {Pid, Res} -> Res; {'EXIT', Pid, Reason} -> - exit(Reason); + error(Reason); Else -> Pid ! Else, wait_on_load_complete(Pid) @@ -442,18 +442,18 @@ init_table(Tab, disc_only_copies, Fun, DetsInfo,Sender) -> ErtsVer = erlang:system_info(version), case DetsInfo of {ErtsVer, DetsData} -> - Res = (catch dets:is_compatible_bchunk_format(Tab, DetsData)), - case Res of - {'EXIT',{undef,[{dets,_,_,_}|_]}} -> - Sender ! {self(), {old_protocol, Tab}}, - dets:init_table(Tab, Fun); %% Old dets version - {'EXIT', What} -> - exit(What); + try dets:is_compatible_bchunk_format(Tab, DetsData) of false -> Sender ! {self(), {old_protocol, Tab}}, dets:init_table(Tab, Fun); %% Old dets version true -> dets:init_table(Tab, Fun, [{format, bchunk}]) + catch + error:{undef,[{dets,_,_,_}|_]} -> + Sender ! {self(), {old_protocol, Tab}}, + dets:init_table(Tab, Fun); %% Old dets version + error:What -> + What end; Old when Old /= false -> Sender ! {self(), {old_protocol, Tab}}, @@ -462,10 +462,10 @@ init_table(Tab, disc_only_copies, Fun, DetsInfo,Sender) -> dets:init_table(Tab, Fun) end; init_table(Tab, _, Fun, _DetsInfo,_) -> - case catch ets:init_table(Tab, Fun) of - true -> - ok; - {'EXIT', Else} -> Else + try + true = ets:init_table(Tab, Fun), + ok + catch _:Else -> {Else, erlang:get_stacktrace()} end. @@ -572,9 +572,9 @@ handle_last({ram_copies, Tab}, _Type, DatBin) -> down(Tab, Storage) -> case Storage of ram_copies -> - catch ?ets_delete_table(Tab); + ?SAFE(?ets_delete_table(Tab)); disc_copies -> - catch ?ets_delete_table(Tab); + ?SAFE(?ets_delete_table(Tab)); disc_only_copies -> TmpFile = mnesia_lib:tab2tmp(Tab), mnesia_lib:dets_sync_close(Tab), @@ -658,26 +658,23 @@ send_table(Pid, Tab, RemoteS) -> {Init, Chunk} = reader_funcs(UseDetsChunk, Tab, Storage, KeysPerTransfer), SendIt = fun() -> - prepare_copy(Pid, Tab, Storage), + {atomic, ok} = prepare_copy(Pid, Tab, Storage), send_more(Pid, 1, Chunk, Init(), Tab), finish_copy(Pid, Tab, Storage, RemoteS) end, - case catch SendIt() of - receiver_died -> + try SendIt() of + {_, receiver_died} -> ok; + {atomic, no_more} -> ok + catch + throw:receiver_died -> cleanup_tab_copier(Pid, Storage, Tab), - unlink(whereis(mnesia_tm)), ok; - {_, receiver_died} -> - unlink(whereis(mnesia_tm)), - ok; - {atomic, no_more} -> - unlink(whereis(mnesia_tm)), - ok; - Reason -> + error:Reason -> %% Prepare failed cleanup_tab_copier(Pid, Storage, Tab), - unlink(whereis(mnesia_tm)), - {error, Reason} + {error, {tab_copier, Tab, {Reason, erlang:get_stacktrace()}}} + after + unlink(whereis(mnesia_tm)) end end. @@ -690,12 +687,7 @@ prepare_copy(Pid, Tab, Storage) -> mnesia_lib:db_fixtable(Storage, Tab, true), ok end, - case mnesia:transaction(Trans) of - {atomic, ok} -> - ok; - {aborted, Reason} -> - exit({tab_copier_prepare, Tab, Reason}) - end. + mnesia:transaction(Trans). update_where_to_write(Tab, Node) -> case val({Tab, access_mode}) of @@ -828,6 +820,6 @@ dat2bin(_Tab, _LocalS, _RemoteS) -> nobin. handle_exit(Pid, Reason) when node(Pid) == node() -> - exit(Reason); + error(Reason); handle_exit(_Pid, _Reason) -> %% Not from our node, this will be handled by ignore. %% mnesia_down soon. diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index 1efb939e00..6658dbeacb 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -98,7 +98,7 @@ init(Parent) -> val(Var) -> case ?catch_val(Var) of - {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_); + {'EXIT', _} -> mnesia_lib:other_val(Var); _VaLuE_ -> _VaLuE_ end. @@ -1001,13 +1001,11 @@ flush_remaining(Ns=[Node | Tail], SkipNode, Res) -> opt_lookup_in_client(lookup_in_client, Oid, Lock) -> {Tab, Key} = Oid, - case catch mnesia_lib:db_get(Tab, Key) of - {'EXIT', _} -> + try mnesia_lib:db_get(Tab, Key) + catch error:_ -> %% Table has been deleted from this node, %% restart the transaction. - #cyclic{op = read, lock = Lock, oid = Oid, lucky = nowhere}; - Val -> - Val + #cyclic{op = read, lock = Lock, oid = Oid, lucky = nowhere} end; opt_lookup_in_client(Val, _Oid, _Lock) -> Val. @@ -1139,11 +1137,10 @@ send_requests([], _X) -> rec_requests([Node | Nodes], Oid, Store) -> Res = l_req_rec(Node, Store), - case catch rlock_get_reply(Node, Store, Oid, Res) of - {'EXIT', Reason} -> - flush_remaining(Nodes, Node, Reason); - _ -> - rec_requests(Nodes, Oid, Store) + try rlock_get_reply(Node, Store, Oid, Res) of + _ -> rec_requests(Nodes, Oid, Store) + catch _:Reason -> + flush_remaining(Nodes, Node, Reason) end; rec_requests([], _Oid, _Store) -> ok. diff --git a/lib/mnesia/src/mnesia_log.erl b/lib/mnesia/src/mnesia_log.erl index 6206a8bdb9..21ad0ffdb6 100644 --- a/lib/mnesia/src/mnesia_log.erl +++ b/lib/mnesia/src/mnesia_log.erl @@ -462,7 +462,7 @@ chunk_log(Cont) -> chunk_log(_Log, eof) -> eof; chunk_log(Log, Cont) -> - case catch disk_log:chunk(Log, Cont) of + case disk_log:chunk(Log, Cont) of {error, Reason} -> fatal("Possibly truncated ~p file: ~p~n", [Log, Reason]); @@ -647,11 +647,11 @@ backup_checkpoint(Name, Opaque, Args) when is_list(Args) -> end. check_backup_args([Arg | Tail], B) -> - case catch check_backup_arg_type(Arg, B) of - {'EXIT', _Reason} -> - {error, {badarg, Arg}}; + try check_backup_arg_type(Arg, B) of B2 -> check_backup_args(Tail, B2) + catch error:_ -> + {error, {badarg, Arg}} end; check_backup_args([], B) -> @@ -674,11 +674,11 @@ check_backup_arg_type(Arg, B) -> backup_master(ClientPid, B) -> process_flag(trap_exit, true), - case catch do_backup_master(B) of - {'EXIT', Reason} -> - ClientPid ! {self(), ClientPid, {error, {'EXIT', Reason}}}; + try do_backup_master(B) of Res -> ClientPid ! {self(), ClientPid, Res} + catch _:Reason -> + ClientPid ! {self(), ClientPid, {error, {'EXIT', Reason}}} end, unlink(ClientPid), exit(normal). @@ -736,10 +736,10 @@ safe_apply(B, What, Args) -> {'EXIT', Pid, R} -> Abort({'EXIT', Pid, R}) after 0 -> Mod = B#backup_args.module, - case catch apply(Mod, What, Args) of + try apply(Mod, What, Args) of {ok, Opaque} -> B#backup_args{opaque=Opaque}; - {error, R} -> Abort(R); - R -> Abort(R) + {error, R} -> Abort(R) + catch _:R -> Abort(R) end end. @@ -748,10 +748,9 @@ abort_write(B, What, Args, Reason) -> Opaque = B#backup_args.opaque, dbg_out("Failed to perform backup. M=~p:F=~p:A=~p -> ~p~n", [Mod, What, Args, Reason]), - case catch apply(Mod, abort_write, [Opaque]) of - {ok, _Res} -> - throw({error, Reason}); - Other -> + try apply(Mod, abort_write, [Opaque]) of + {ok, _Res} -> throw({error, Reason}) + catch _:Other -> error("Failed to abort backup. ~p:~p~p -> ~p~n", [Mod, abort_write, [Opaque], Other]), throw({error, Reason}) @@ -892,10 +891,8 @@ tab_receiver(Pid, B, Tab, RecName, Slot) -> end. rec_filter(B, schema, _RecName, Recs) -> - case catch mnesia_bup:refresh_cookie(Recs, B#backup_args.cookie) of - Recs2 when is_list(Recs2) -> - Recs2; - {error, _Reason} -> + try mnesia_bup:refresh_cookie(Recs, B#backup_args.cookie) + catch throw:{error, _Reason} -> %% No schema table cookie Recs end; @@ -1006,13 +1003,14 @@ add_recs([{{Tab, _Key}, Val, delete_object} | Rest], N) -> add_recs(Rest, N+1); add_recs([{{Tab, Key}, Val, update_counter} | Rest], N) -> {RecName, Incr} = Val, - case catch ets:update_counter(Tab, Key, Incr) of - CounterVal when is_integer(CounterVal) -> - ok; - _ when Incr < 0 -> + try + CounterVal = ets:update_counter(Tab, Key, Incr), + true = (CounterVal >= 0) + catch + error:_ when Incr < 0 -> Zero = {RecName, Key, 0}, true = ets:insert(Tab, Zero); - _ -> + error:_ -> Zero = {RecName, Key, Incr}, true = ets:insert(Tab, Zero) end, diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl index a0e0e630ec..14b1ab5c1a 100644 --- a/lib/mnesia/src/mnesia_monitor.erl +++ b/lib/mnesia/src/mnesia_monitor.erl @@ -268,7 +268,7 @@ init([Parent]) -> set(version, Version), dbg_out("Version: ~p~n", [Version]), - case catch process_config_args(env()) of + try process_config_args(env()) of ok -> mnesia_lib:set({'$$$_report', current_pos}, 0), Level = mnesia_lib:val(debug), @@ -288,8 +288,8 @@ init([Parent]) -> set(pending_checkpoints, []), set(pending_checkpoint_pids, []), - {ok, #state{supervisor = Parent}}; - {'EXIT', Reason} -> + {ok, #state{supervisor = Parent}} + catch _:Reason -> mnesia_lib:report_fatal("Bad configuration: ~p~n", [Reason]), {stop, {bad_config, Reason}} end. @@ -323,25 +323,24 @@ non_empty_dir() -> %%---------------------------------------------------------------------- handle_call({mktab, Tab, Args}, _From, State) -> - case catch ?ets_new_table(Tab, Args) of - {'EXIT', ExitReason} -> + try ?ets_new_table(Tab, Args) of + Reply -> + {reply, Reply, State} + catch error:ExitReason -> Msg = "Cannot create ets table", Reason = {system_limit, Msg, Tab, Args, ExitReason}, fatal("~p~n", [Reason]), - {noreply, State}; - Reply -> - {reply, Reply, State} + {noreply, State} end; handle_call({unsafe_mktab, Tab, Args}, _From, State) -> - case catch ?ets_new_table(Tab, Args) of - {'EXIT', ExitReason} -> - {reply, {error, ExitReason}, State}; + try ?ets_new_table(Tab, Args) of Reply -> {reply, Reply, State} + catch error:ExitReason -> + {reply, {error, ExitReason}, State} end; - handle_call({open_dets, Tab, Args}, _From, State) -> case mnesia_lib:dets_sync_open(Tab, Args) of {ok, Tab} -> @@ -546,7 +545,7 @@ handle_info({'EXIT', Pid, fatal}, State) when node(Pid) == node() -> %% is in progress %% exit(State#state.supervisor, shutdown), %% It is better to kill an innocent process - catch exit(whereis(mnesia_locker), kill), + ?SAFE(exit(whereis(mnesia_locker), kill)), {noreply, State}; handle_info(Msg = {'EXIT',Pid,_}, State) -> @@ -727,11 +726,8 @@ default_env(send_compressed) -> 0. check_type(Env, Val) -> - case catch do_check_type(Env, Val) of - {'EXIT', _Reason} -> - exit({bad_config, Env, Val}); - NewVal -> - NewVal + try do_check_type(Env, Val) + catch error:_ -> exit({bad_config, Env, Val}) end. do_check_type(access_module, A) when is_atom(A) -> A; @@ -781,12 +777,12 @@ media(opt_disc) -> opt_disc; media(ram) -> ram. patch_env(Env, Val) -> - case catch do_check_type(Env, Val) of - {'EXIT', _Reason} -> - {error, {bad_type, Env, Val}}; + try do_check_type(Env, Val) of NewVal -> application_controller:set_env(mnesia, Env, NewVal), NewVal + catch error:_ -> + {error, {bad_type, Env, Val}} end. detect_partitioned_network(Mon, Node) -> diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl index eeb4fa0ced..aa567a23cb 100644 --- a/lib/mnesia/src/mnesia_recover.erl +++ b/lib/mnesia/src/mnesia_recover.erl @@ -178,11 +178,8 @@ log_decision(D) -> val(Var) -> case ?catch_val(Var) of - {'EXIT', Reason} -> - case mnesia_lib:other_val(Var) of - error -> mnesia_lib:pr_other(Var, Reason); - Val -> Val - end; + {'EXIT', _Reason} -> + mnesia_lib:other_val(Var); Value -> Value end. @@ -373,11 +370,8 @@ log_master_nodes2([], _UseDir, IsRunning, WorstRes) -> get_master_node_info() -> Tab = mnesia_decision, Pat = {master_nodes, '_', '_'}, - case catch mnesia_lib:db_match_object(ram_copies,Tab, Pat) of - {'EXIT', _} -> - []; - Masters -> - Masters + try mnesia_lib:db_match_object(ram_copies,Tab, Pat) + catch error:_ -> [] end. get_master_node_tables() -> @@ -385,9 +379,8 @@ get_master_node_tables() -> [Tab || {master_nodes, Tab, _Nodes} <- Masters]. get_master_nodes(Tab) -> - case catch ?ets_lookup_element(mnesia_decision, Tab, 3) of - {'EXIT', _} -> []; - Nodes -> Nodes + try ?ets_lookup_element(mnesia_decision, Tab, 3) + catch error:_ -> [] end. %% Determine what has happened to the transaction @@ -485,8 +478,6 @@ load_decision_tab() -> load_decision_tab(Cont, load_decision_tab), mnesia_log:close_decision_tab(). -load_decision_tab(eof, _InitBy) -> - ok; load_decision_tab(Cont, InitBy) -> case mnesia_log:chunk_decision_tab(Cont) of {Cont2, Decisions} -> @@ -519,8 +510,6 @@ dump_decision_log(InitBy) -> Cont = mnesia_log:prepare_decision_log_dump(), perform_dump_decision_log(Cont, InitBy). -perform_dump_decision_log(eof, _InitBy) -> - confirm_decision_log_dump(); perform_dump_decision_log(Cont, InitBy) when InitBy == startup -> case mnesia_log:chunk_decision_log(Cont) of {Cont2, Decisions} -> @@ -1024,7 +1013,7 @@ decision(Tid) -> decision(Tid, tabs()). decision(Tid, [Tab | Tabs]) -> - case catch ?ets_lookup(Tab, Tid) of + try ?ets_lookup(Tab, Tid) of [D] when is_record(D, decision) -> D; [C] when is_record(C, transient_decision) -> @@ -1034,8 +1023,8 @@ decision(Tid, [Tab | Tabs]) -> ram_nodes = [] }; [] -> - decision(Tid, Tabs); - {'EXIT', _} -> + decision(Tid, Tabs) + catch error:_ -> %% Recently switched transient decision table decision(Tid, Tabs) end; @@ -1046,11 +1035,8 @@ outcome(Tid, Default) -> outcome(Tid, Default, tabs()). outcome(Tid, Default, [Tab | Tabs]) -> - case catch ?ets_lookup_element(Tab, Tid, 3) of - {'EXIT', _} -> - outcome(Tid, Default, Tabs); - Val -> - Val + try ?ets_lookup_element(Tab, Tid, 3) + catch error:_ -> outcome(Tid, Default, Tabs) end; outcome(_Tid, Default, []) -> Default. diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl index 52180cfcf5..4c8234bbc7 100644 --- a/lib/mnesia/src/mnesia_schema.erl +++ b/lib/mnesia/src/mnesia_schema.erl @@ -151,7 +151,7 @@ exit_on_error(GoodRes) -> val(Var) -> case ?catch_val(Var) of - {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); + {'EXIT', _} -> mnesia_lib:other_val(Var); Value -> Value end. @@ -2151,14 +2151,14 @@ prepare_op(_Tid, {op, transform, Fun, TabDef}, _WaitFor) -> mnesia_lib:db_fixtable(Storage, Tab, true), Key = mnesia_lib:db_first(Tab), Op = {op, transform, Fun, TabDef}, - case catch transform_objs(Fun, Tab, RecName, - Key, NewArity, Storage, Type, [Op]) of - {'EXIT', Reason} -> - mnesia_lib:db_fixtable(Storage, Tab, false), - exit({"Bad transform function", Tab, Fun, node(), Reason}); + try transform_objs(Fun, Tab, RecName, Key, + NewArity, Storage, Type, [Op]) of Objs -> mnesia_lib:db_fixtable(Storage, Tab, false), {true, Objs, mandatory} + catch _:Reason -> + mnesia_lib:db_fixtable(Storage, Tab, false), + exit({"Bad transform function", Tab, Fun, node(), Reason}) end end; @@ -2342,7 +2342,7 @@ undo_prepare_commit(Tid, Commit) -> ignore; Ops -> %% Catch to allow failure mnesia_controller may not be started - catch mnesia_controller:release_schema_commit_lock(), + ?SAFE(mnesia_controller:release_schema_commit_lock()), undo_prepare_ops(Tid, Ops) end, Commit. @@ -2489,14 +2489,14 @@ ram_delete_table(Tab, Storage) -> %% delete possible index files and data ..... %% Got to catch this since if no info has been set in the %% mnesia_gvar it will crash - catch mnesia_index:del_transient(Tab, Storage), + ?CATCH(mnesia_index:del_transient(Tab, Storage)), case ?catch_val({Tab, {index, snmp}}) of {'EXIT', _} -> ignore; Etab -> - catch mnesia_snmp_hook:delete_table(Tab, Etab) + ?SAFE(mnesia_snmp_hook:delete_table(Tab, Etab)) end, - catch ?ets_delete_table(Tab) + ?SAFE(?ets_delete_table(Tab)) end. purge_dir(Dir, KeepFiles) -> @@ -2584,10 +2584,7 @@ info2(_, []) -> io:format("~n", []). get_table_properties(Tab) -> - case catch mnesia_lib:db_match_object(ram_copies, - mnesia_gvar, {{Tab, '_'}, '_'}) of - {'EXIT', _} -> - mnesia:abort({no_exists, Tab, all}); + try mnesia_lib:db_match_object(ram_copies, mnesia_gvar, {{Tab, '_'}, '_'}) of RawGvar -> case [{Item, Val} || {{_Tab, Item}, Val} <- RawGvar] of [] -> @@ -2598,6 +2595,8 @@ get_table_properties(Tab) -> Master = {master_nodes, mnesia:table_info(Tab, master_nodes)}, lists:sort([Size, Memory, Master | Gvar]) end + catch error:_ -> + mnesia:abort({no_exists, Tab, all}) end. %%%%%%%%%%% RESTORE %%%%%%%%%%% @@ -2620,15 +2619,15 @@ restore(_Opaque, BadArg) -> {aborted, {badarg, BadArg}}. restore(Opaque, Args, Module) when is_list(Args), is_atom(Module) -> InitR = #r{opaque = Opaque, module = Module}, - case catch lists:foldl(fun check_restore_arg/2, InitR, Args) of + try lists:foldl(fun check_restore_arg/2, InitR, Args) of R when is_record(R, r) -> case mnesia_bup:read_schema(R#r.module, Opaque) of {error, Reason} -> {aborted, Reason}; BupSchema -> schema_transaction(fun() -> do_restore(R, BupSchema) end) - end; - {'EXIT', Reason} -> + end + catch exit:Reason -> {aborted, Reason} end; restore(_Opaque, Args, Module) -> @@ -3073,15 +3072,13 @@ do_make_merge_schema(Node, NeedsConv, RemoteCs = #cstruct{}) -> %% Returns a new cstruct or issues a fatal error merge_cstructs(Cs, RemoteCs, Force) -> verify_cstruct(Cs), - case catch do_merge_cstructs(Cs, RemoteCs, Force) of - {'EXIT', {aborted, _Reason}} when Force == true -> - Cs; - {'EXIT', Reason} -> - exit(Reason); + try do_merge_cstructs(Cs, RemoteCs, Force) of MergedCs when is_record(MergedCs, cstruct) -> - MergedCs; - Other -> - throw(Other) + MergedCs + catch exit:{aborted, _Reason} when Force == true -> + Cs; + exit:Reason -> exit(Reason); + error:Reason -> exit(Reason) end. do_merge_cstructs(Cs, RemoteCs, Force) -> diff --git a/lib/mnesia/src/mnesia_snmp_hook.erl b/lib/mnesia/src/mnesia_snmp_hook.erl index 256f83b029..c76cf89ebb 100644 --- a/lib/mnesia/src/mnesia_snmp_hook.erl +++ b/lib/mnesia/src/mnesia_snmp_hook.erl @@ -30,15 +30,6 @@ -include("mnesia.hrl"). -val(Var) -> - case ?catch_val(Var) of - {'EXIT', _ReASoN_} -> - case mnesia_lib:other_val(Var) of - error -> mnesia_lib:pr_other(Var, _ReASoN_); - Val -> Val - end; - _VaLuE_ -> _VaLuE_ - end. check_ustruct([]) -> true; %% default value, not SNMP'ified @@ -85,12 +76,12 @@ delete_table(_MnesiaTab, Tree) -> %%----------------------------------------------------------------- update({clear_table, MnesiaTab}) -> - Tree = val({MnesiaTab, {index, snmp}}), + Tree = mnesia_lib:val({MnesiaTab, {index, snmp}}), b_clear(Tree), ok; update({Op, MnesiaTab, MnesiaKey, SnmpKey}) -> - Tree = val({MnesiaTab, {index, snmp}}), + Tree = mnesia_lib:val({MnesiaTab, {index, snmp}}), update(Op, Tree, MnesiaKey, SnmpKey). update(Op, Tree, MnesiaKey, SnmpKey) -> @@ -120,7 +111,7 @@ update(Op, Tree, MnesiaKey, SnmpKey) -> %%----------------------------------------------------------------- key_to_oid(Tab,Key) -> - Types = val({Tab,snmp}), + Types = mnesia_lib:val({Tab,snmp}), key_to_oid(Tab, Key, Types). key_to_oid(Tab, Key, [{key, Types}]) -> @@ -144,7 +135,7 @@ keys_to_oid(N, Key, Oid, Types) -> %% This can be lookup up in tree but that might be on a remote node. %% It's probably faster to look it up, but use when it migth be remote oid_to_key(Oid, Tab) -> - [{key, Types}] = val({Tab,snmp}), + [{key, Types}] = mnesia_lib:val({Tab,snmp}), oid_to_key_1(Types, Oid). oid_to_key_1(integer, [Key]) -> Key; diff --git a/lib/mnesia/src/mnesia_subscr.erl b/lib/mnesia/src/mnesia_subscr.erl index 866a57e370..c39edea9e3 100644 --- a/lib/mnesia/src/mnesia_subscr.erl +++ b/lib/mnesia/src/mnesia_subscr.erl @@ -186,11 +186,11 @@ patch_record(Tab, Obj) -> end. what(Tab, Tid, {RecName, Key}, delete, undefined) -> - case catch mnesia_lib:db_get(Tab, Key) of - Old when is_list(Old) -> %% Op only allowed for set table. - {mnesia_table_event, {delete, Tab, {RecName, Key}, Old, Tid}}; - _ -> - %% Record just deleted by a dirty_op or + try mnesia_lib:db_get(Tab, Key) of + Old -> %% Op only allowed for set table. + {mnesia_table_event, {delete, Tab, {RecName, Key}, Old, Tid}} + catch error:_ -> + %% Record just deleted by a dirty_op or %% the whole table has been deleted ignore end; @@ -199,10 +199,10 @@ what(Tab, Tid, Obj, delete, Old) -> what(Tab, Tid, Obj, delete_object, _Old) -> {mnesia_table_event, {delete, Tab, Obj, [Obj], Tid}}; what(Tab, Tid, Obj, write, undefined) -> - case catch mnesia_lib:db_get(Tab, element(2, Obj)) of - Old when is_list(Old) -> - {mnesia_table_event, {write, Tab, Obj, Old, Tid}}; - {'EXIT', _} -> + try mnesia_lib:db_get(Tab, element(2, Obj)) of + Old -> + {mnesia_table_event, {write, Tab, Obj, Old, Tid}} + catch error:_ -> ignore end; what(Tab, Tid, Obj, write, Old) -> @@ -386,12 +386,12 @@ activate(ClientPid, What, Var, OldSubscribers, SubscrTab) -> case lists:member(ClientPid, Old) of false -> %% Don't care about checking old links - case catch link(ClientPid) of + try link(ClientPid) of true -> ?ets_insert(SubscrTab, {ClientPid, What}), add_subscr(Var, What, ClientPid), - {ok, node()}; - {'EXIT', _Reason} -> + {ok, node()} + catch error:_ -> {error, {no_exists, ClientPid}} end; true -> @@ -443,11 +443,10 @@ add_subscr({Tab, commit_work}, What, Pid) -> deactivate(ClientPid, What, Var, SubscrTab) -> ?ets_match_delete(SubscrTab, {ClientPid, What}), - case catch ?ets_lookup_element(SubscrTab, ClientPid, 1) of - List when is_list(List) -> - ignore; - {'EXIT', _} -> - unlink(ClientPid) + try + ?ets_lookup_element(SubscrTab, ClientPid, 1), + ignore + catch error:_ -> unlink(ClientPid) end, try del_subscr(Var, What, ClientPid), diff --git a/lib/mnesia/src/mnesia_text.erl b/lib/mnesia/src/mnesia_text.erl index 0906d18da9..794e633238 100644 --- a/lib/mnesia/src/mnesia_text.erl +++ b/lib/mnesia/src/mnesia_text.erl @@ -84,8 +84,12 @@ validate_tab({Tabname, RecName, List}) -> validate_tab(_) -> error(badtab). make_tabs([{Tab, Def} | Tail]) -> - case catch mnesia:table_info(Tab, where_to_read) of - {'EXIT', _} -> %% non-existing table + try mnesia:table_info(Tab, where_to_read) of + Node -> + io:format("** Table ~w 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" @@ -95,11 +99,7 @@ make_tabs([{Tab, Def} | Tail]) -> _ -> io:format("New table ~w~n", [Tab]), make_tabs(Tail) - end; - Node -> - io:format("** Table ~w already exists on ~p, just entering data~n", - [Tab, Node]), - make_tabs(Tail) + end end; make_tabs([]) -> @@ -118,11 +118,9 @@ load_data(L) -> parse(File) -> case file(File) of {ok, Terms} -> - case catch collect(Terms) of - {error, X} -> - {error, X}; - Other -> - {ok, Other} + try collect(Terms) of + Other -> {ok, Other} + catch throw:Error -> Error end; Other -> Other diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index af658150da..b4b46228e9 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -51,6 +51,7 @@ ]). -include("mnesia.hrl"). + -import(mnesia_lib, [set/2]). -import(mnesia_lib, [fatal/2, verbose/2, dbg_out/2]). @@ -119,7 +120,7 @@ init(Parent) -> val(Var) -> case ?catch_val(Var) of - {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_); + {'EXIT', _} -> mnesia_lib:other_val(Var); _VaLuE_ -> _VaLuE_ end. @@ -224,11 +225,7 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor= end; {From, start_outer} -> %% Create and associate ets_tab with Tid - case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of - {'EXIT', Reason} -> %% system limit - Msg = "Cannot create an ets table for the " - "local transaction store", - reply(From, {error, {system_limit, Msg, Reason}}, State); + try ?ets_new_table(mnesia_trans_store, [bag, public]) of Etab -> tmlink(From), C = mnesia_recover:incr_trans_tid_serial(), @@ -237,6 +234,10 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor= A2 = gb_trees:insert(Tid,[Etab],Coordinators), S2 = State#state{coordinators = A2}, reply(From, {new_tid, Tid, Etab}, S2) + catch error:Reason -> %% system limit + Msg = "Cannot create an ets table for the " + "local transaction store", + reply(From, {error, {system_limit, Msg, Reason}}, State) end; {From, {ask_commit, Protocol, Tid, Commit, DiscNs, RamNs}} -> @@ -339,15 +340,15 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor= end; {From, {add_store, Tid}} -> %% new store for nested transaction - case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of - {'EXIT', Reason} -> %% system limit - Msg = "Cannot create an ets table for a nested " - "local transaction store", - reply(From, {error, {system_limit, Msg, Reason}}, State); + try ?ets_new_table(mnesia_trans_store, [bag, public]) of Etab -> A2 = add_coord_store(Coordinators, Tid, Etab), reply(From, {new_store, Etab}, State#state{coordinators = A2}) + catch error:Reason -> %% system limit + Msg = "Cannot create an ets table for a nested " + "local transaction store", + reply(From, {error, {system_limit, Msg, Reason}}, State) end; {From, {del_store, Tid, Current, Obsolete, PropagateStore}} -> @@ -471,13 +472,13 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor= do_sync_dirty(From, Tid, Commit, _Tab) -> ?eval_debug_fun({?MODULE, sync_dirty, pre}, [{tid, Tid}]), - Res = (catch do_dirty(Tid, Commit)), + Res = do_dirty(Tid, Commit), ?eval_debug_fun({?MODULE, sync_dirty, post}, [{tid, Tid}]), From ! {?MODULE, node(), {dirty_res, Res}}. do_async_dirty(Tid, Commit, _Tab) -> ?eval_debug_fun({?MODULE, async_dirty, pre}, [{tid, Tid}]), - catch do_dirty(Tid, Commit), + do_dirty(Tid, Commit), ?eval_debug_fun({?MODULE, async_dirty, post}, [{tid, Tid}]). @@ -501,7 +502,7 @@ process_dirty_queue(_Tab, []) -> []. prepare_pending_coordinators([{Tid, [Store | _Etabs]} | Coords], IgnoreNew) -> - case catch ?ets_lookup(Store, pending) of + try ?ets_lookup(Store, pending) of [] -> prepare_pending_coordinators(Coords, IgnoreNew); [Pending] -> @@ -511,8 +512,8 @@ prepare_pending_coordinators([{Tid, [Store | _Etabs]} | Coords], IgnoreNew) -> true -> ignore end, - prepare_pending_coordinators(Coords, IgnoreNew); - {'EXIT', _} -> + prepare_pending_coordinators(Coords, IgnoreNew) + catch error:_ -> prepare_pending_coordinators(Coords, IgnoreNew) end; prepare_pending_coordinators([], _IgnoreNew) -> @@ -573,11 +574,7 @@ recover_coordinator(Tid, Etabs) -> Store = hd(Etabs), CheckNodes = get_elements(nodes,Store), TellNodes = CheckNodes -- [node()], - case catch arrange(Tid, Store, async) of - {'EXIT', Reason} -> - dbg_out("Recovery of coordinator ~p failed:~n", [Tid, Reason]), - Protocol = asym_trans, - tell_outcome(Tid, Protocol, node(), CheckNodes, TellNodes); + try arrange(Tid, Store, async) of {_N, Prep} -> %% Tell the participants about the outcome Protocol = Prep#prep.protocol, @@ -596,6 +593,11 @@ recover_coordinator(Tid, Etabs) -> false -> %% When killed before store havn't been copied to ok %% to the new nested trans store. end + catch _:Reason -> + dbg_out("Recovery of coordinator ~p failed:~n", + [Tid, {Reason, erlang:get_stacktrace()}]), + Protocol = asym_trans, + tell_outcome(Tid, Protocol, node(), CheckNodes, TellNodes) end, erase_ets_tabs(Etabs), transaction_terminated(Tid), @@ -724,33 +726,25 @@ non_transaction(OldState={_,_,Trans}, Fun, Args, ActivityKind, Mod) _ -> async end, case transaction(OldState, Fun, Args, infinity, Mod, Kind) of - {atomic, Res} -> - Res; - {aborted,Res} -> - exit(Res) + {atomic, Res} -> Res; + {aborted,Res} -> exit(Res) end; non_transaction(OldState, Fun, Args, ActivityKind, Mod) -> Id = {ActivityKind, self()}, NewState = {Mod, Id, non_transaction}, put(mnesia_activity_state, NewState), - %% I Want something uniqe here, references are expensive - Ref = mNeSia_nOn_TrAnSacTioN, - RefRes = (catch {Ref, apply(Fun, Args)}), - case OldState of - undefined -> erase(mnesia_activity_state); - _ -> put(mnesia_activity_state, OldState) - end, - case RefRes of - {Ref, Res} -> - case Res of - {'EXIT', Reason} -> exit(Reason); - {aborted, Reason} -> mnesia:abort(Reason); - _ -> Res - end; - {'EXIT', Reason} -> - exit(Reason); - Throw -> - throw(Throw) + try apply(Fun, Args) of + {'EXIT', Reason} -> exit(Reason); + {aborted, Reason} -> mnesia:abort(Reason); + Res -> Res + catch + throw:Throw -> throw(Throw); + _:Reason -> exit(Reason) + after + case OldState of + undefined -> erase(mnesia_activity_state); + _ -> put(mnesia_activity_state, OldState) + end end. transaction(OldTidTs, Fun, Args, Retries, Mod, Type) -> @@ -810,23 +804,28 @@ insert_objs([], _Tab) -> ok. execute_transaction(Fun, Args, Factor, Retries, Type) -> - case catch apply_fun(Fun, Args, Type) of - {'EXIT', Reason} -> - check_exit(Fun, Args, Factor, Retries, Reason, Type); + try apply_fun(Fun, Args, Type) of {atomic, Value} -> mnesia_lib:incr_counter(trans_commits), erase(mnesia_activity_state), %% no need to clear locks, already done by commit ... %% Flush any un processed mnesia_down messages we might have flush_downs(), - catch unlink(whereis(?MODULE)), + ?SAFE(unlink(whereis(?MODULE))), {atomic, Value}; + {do_abort, Reason} -> + check_exit(Fun, Args, Factor, Retries, {aborted, Reason}, Type); {nested_atomic, Value} -> mnesia_lib:incr_counter(trans_commits), - {atomic, Value}; - Value -> %% User called throw + {atomic, Value} + catch throw:Value -> %% User called throw Reason = {aborted, {throw, Value}}, - return_abort(Fun, Args, Reason) + return_abort(Fun, Args, Reason); + error:Reason -> + ST = erlang:get_stacktrace(), + check_exit(Fun, Args, Factor, Retries, {Reason,ST}, Type); + _:Reason -> + check_exit(Fun, Args, Factor, Retries, Reason, Type) end. apply_fun(Fun, Args, Type) -> @@ -836,10 +835,10 @@ apply_fun(Fun, Args, Type) -> {atomic, Result}; do_commit_nested -> {nested_atomic, Result}; - {do_abort, {aborted, Reason}} -> - {'EXIT', {aborted, Reason}}; - {do_abort, Reason} -> - {'EXIT', {aborted, Reason}} + {do_abort, {aborted, Reason}} -> + {do_abort, Reason}; + {do_abort, _} = Abort -> + Abort end. check_exit(Fun, Args, Factor, Retries, Reason, Type) -> @@ -943,7 +942,7 @@ return_abort(Fun, Args, Reason) -> OldStore = Ts#tidstore.store, Nodes = get_elements(nodes, OldStore), intercept_friends(Tid, Ts), - catch mnesia_lib:incr_counter(trans_failures), + ?SAFE(mnesia_lib:incr_counter(trans_failures)), Level = Ts#tidstore.level, if Level == 1 -> @@ -951,7 +950,7 @@ return_abort(Fun, Args, Reason) -> ?MODULE ! {delete_transaction, Tid}, erase(mnesia_activity_state), flush_downs(), - catch unlink(whereis(?MODULE)), + ?SAFE(unlink(whereis(?MODULE))), {aborted, mnesia_lib:fix_error(Reason)}; true -> %% Nested transaction @@ -1005,11 +1004,11 @@ erase_activity_id() -> erase(mnesia_activity_state). get_elements(Type,Store) -> - case catch ?ets_lookup(Store, Type) of + try ?ets_lookup(Store, Type) of [] -> []; [{_,Val}] -> [Val]; - {'EXIT', _} -> []; Vals -> [Val|| {_,Val} <- Vals] + catch error:_ -> [] end. opt_propagate_store(_Current, _Obsolete, false) -> @@ -1032,7 +1031,7 @@ intercept_friends(_Tid, Ts) -> intercept_best_friend([],_) -> ok; intercept_best_friend([{stop,Fun} | R],Ignore) -> - catch Fun(), + ?CATCH(Fun()), intercept_best_friend(R,Ignore); intercept_best_friend([Pid | R],false) -> Pid ! {activity_ended, undefined, self()}, @@ -1046,25 +1045,12 @@ wait_for_best_friend(Pid, Timeout) -> {'EXIT', Pid, _} -> ok; {activity_ended, _, Pid} -> ok after Timeout -> - case my_process_is_alive(Pid) of + case erlang:is_process_alive(Pid) of true -> wait_for_best_friend(Pid, 1000); false -> ok end end. -my_process_is_alive(Pid) -> - case catch erlang:is_process_alive(Pid) of % New BIF in R5 - true -> - true; - false -> - false; - {'EXIT', _} -> % Pre R5 backward compatibility - case process_info(Pid, message_queue_len) of - undefined -> false; - _ -> true - end - end. - dirty(Protocol, Item) -> {{Tab, Key}, _Val, _Op} = Item, Tid = {dirty, self()}, @@ -1144,18 +1130,8 @@ arrange(Tid, Store, Type) -> async -> #prep{protocol = sym_trans, records = Recs}; sync -> #prep{protocol = sync_sym_trans, records = Recs} end, - case catch do_arrange(Tid, Store, Key, Prep, N) of - {'EXIT', Reason} -> - dbg_out("do_arrange failed ~p ~p~n", [Reason, Tid]), - case Reason of - {aborted, R} -> - mnesia:abort(R); - _ -> - mnesia:abort(Reason) - end; - {New, Prepared} -> - {New, Prepared#prep{records = reverse(Prepared#prep.records)}} - end. + {New, Prepared} = do_arrange(Tid, Store, Key, Prep, N), + {New, Prepared#prep{records = reverse(Prepared#prep.records)}}. reverse([]) -> []; @@ -1522,7 +1498,7 @@ multi_commit(asym_trans, Majority, Tid, CR, Store) -> Pending = mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs), ?ets_insert(Store, Pending), {WaitFor, Local} = ask_commit(asym_trans, Tid, CR2, DiscNs, RamNs), - SchemaPrep = (catch mnesia_schema:prepare_commit(Tid, Local, {coord, WaitFor})), + SchemaPrep = ?CATCH(mnesia_schema:prepare_commit(Tid, Local, {coord, WaitFor})), {Votes, Pids} = rec_all(WaitFor, Tid, do_commit, []), ?eval_debug_fun({?MODULE, multi_commit_asym_got_votes}, @@ -1589,7 +1565,7 @@ rec_acc_pre_commit([Pid | Tail], Tid, Store, Commit, Res, DumperMode, GoodPids, SchemaAckPids); {mnesia_down, Node} when Node == node(Pid) -> AbortRes = {do_abort, {bad_commit, Node}}, - catch Pid ! {Tid, AbortRes}, %% Tell him that he has died + ?SAFE(Pid ! {Tid, AbortRes}), %% Tell him that he has died rec_acc_pre_commit(Tail, Tid, Store, Commit, AbortRes, DumperMode, GoodPids, SchemaAckPids) end; @@ -1666,7 +1642,7 @@ commit_participant(Coord, Tid, C = #commit{}, DiscNs, RamNs) -> commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) -> ?eval_debug_fun({?MODULE, commit_participant, pre}, [{tid, Tid}]), - case catch mnesia_schema:prepare_commit(Tid, C0, {part, Coord}) of + try mnesia_schema:prepare_commit(Tid, C0, {part, Coord}) of {Modified, C = #commit{}, DumperMode} -> %% If we can not find any local unclear decision %% we should presume abort at startup recovery @@ -1742,9 +1718,8 @@ commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) -> reply(Coord, {do_abort, Tid, self(), {bad_commit,internal}}), verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~p~n", [Tid, Msg]) - end; - - {'EXIT', Reason} -> + end + catch _:Reason -> ?eval_debug_fun({?MODULE, commit_participant, vote_no}, [{tid, Tid}]), reply(Coord, {vote_no, Tid, Reason}), @@ -1790,22 +1765,20 @@ do_commit(Tid, C, DumperMode) -> %% Update the items do_update(Tid, Storage, [Op | Ops], OldRes) -> - case catch do_update_op(Tid, Storage, Op) of - ok -> - do_update(Tid, Storage, Ops, OldRes); - {'EXIT', Reason} -> + try do_update_op(Tid, Storage, Op) of + ok -> do_update(Tid, Storage, Ops, OldRes); + NewRes -> do_update(Tid, Storage, Ops, NewRes) + catch _:Reason -> %% This may only happen when we recently have %% deleted our local replica, changed storage_type %% or transformed table %% BUGBUG: Updates may be lost if storage_type is changed. %% 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", - [Tid, Op, Reason]), - do_update(Tid, Storage, Ops, OldRes); - NewRes -> - do_update(Tid, Storage, Ops, NewRes) + [Tid, Op, {Reason, ST}]), + do_update(Tid, Storage, Ops, OldRes) end; do_update(_Tid, _Storage, [], Res) -> Res. @@ -1821,14 +1794,15 @@ do_update_op(Tid, Storage, {{Tab, K}, Val, delete}) -> do_update_op(Tid, Storage, {{Tab, K}, {RecName, Incr}, update_counter}) -> {NewObj, OldObjs} = - case catch mnesia_lib:db_update_counter(Storage, Tab, K, Incr) of - NewVal when is_integer(NewVal), NewVal >= 0 -> - {{RecName, K, NewVal}, [{RecName, K, NewVal - Incr}]}; - _ when Incr > 0 -> + try + NewVal = mnesia_lib:db_update_counter(Storage, Tab, K, Incr), + true = is_integer(NewVal) andalso (NewVal >= 0), + {{RecName, K, NewVal}, [{RecName, K, NewVal - Incr}]} + catch error:_ when Incr > 0 -> New = {RecName, K, Incr}, mnesia_lib:db_put(Storage, Tab, New), {New, []}; - _ -> + error:_ -> Zero = {RecName, K, 0}, mnesia_lib:db_put(Storage, Tab, Zero), {Zero, []} @@ -1913,16 +1887,14 @@ commit_clear([H|R], Tid, Tab, K, Obj) do_snmp(_, []) -> ok; do_snmp(Tid, [Head | Tail]) -> - case catch mnesia_snmp_hook:update(Head) of - {'EXIT', Reason} -> + try mnesia_snmp_hook:update(Head) + catch _:Reason -> %% This should only happen when we recently have %% deleted our local replica or recently deattached %% the snmp table - + ST = erlang:get_stacktrace(), verbose("do_snmp in ~w failed: ~p -> {'EXIT', ~p}~n", - [Tid, Head, Reason]); - ok -> - ignore + [Tid, Head, {Reason, ST}]) end, do_snmp(Tid, Tail). @@ -2093,7 +2065,7 @@ rec_all([Node | Tail], Tid, Res, Pids) -> %% Make sure that mnesia_tm knows it has died %% it may have been restarted Abort = {do_abort, {bad_commit, Node}}, - catch {?MODULE, Node} ! {Tid, Abort}, + ?SAFE({?MODULE, Node} ! {Tid, Abort}), rec_all(Tail, Tid, Abort, Pids) end; rec_all([], _Tid, Res, Pids) -> diff --git a/lib/mnesia/test/mnesia_config_backup.erl b/lib/mnesia/test/mnesia_config_backup.erl index 0916e255e2..a7d8c04a45 100644 --- a/lib/mnesia/test/mnesia_config_backup.erl +++ b/lib/mnesia/test/mnesia_config_backup.erl @@ -90,7 +90,8 @@ open_read(Name) -> List = lists:reverse(ReverseList), {ok, #backup{name = Name, mode = read, items = List}}; {error, Reason} -> - {error, {open_read, Reason}} + %% {error, {open_read, Reason}} + {Reason, error} %% Testing error handling in mnesia end. read(Opaque) when Opaque#backup.mode == read -> diff --git a/lib/mnesia/test/mnesia_config_test.erl b/lib/mnesia/test/mnesia_config_test.erl index c495bce63f..a8fb93b28e 100644 --- a/lib/mnesia/test/mnesia_config_test.erl +++ b/lib/mnesia/test/mnesia_config_test.erl @@ -37,7 +37,6 @@ dump_log_update_in_place/1, event_module/1, - ignore_fallback_at_startup/1, inconsistent_database/1, max_wait_for_decision/1, send_compressed/1, @@ -104,7 +103,7 @@ all() -> [access_module, auto_repair, backup_module, debug, dir, dump_log_load_regulation, {group, dump_log_thresholds}, dump_log_update_in_place, - event_module, ignore_fallback_at_startup, + event_module, inconsistent_database, max_wait_for_decision, send_compressed, app_test, {group, schema_config}, unknown_config]. @@ -317,11 +316,17 @@ backup_module(Config) when is_list(Config) -> ?match([], mnesia_test_lib:start_mnesia(Nodes, [test_table, test_table2])), %% Now check newly started tables - ?match({atomic, [1,2]}, + ?match({atomic, [1,2]}, mnesia:transaction(fun() -> lists:sort(mnesia:all_keys(test_table)) end)), - ?match({atomic, [3,4]}, + ?match({atomic, [3,4]}, mnesia:transaction(fun() -> lists:sort(mnesia:all_keys(test_table2)) end)), - + + %% Test some error cases + mnesia:set_debug_level(debug), + ?match({error, _}, mnesia:install_fallback("NonExisting.FILE")), + ?match({error, _}, mnesia:install_fallback(filename:join(mnesia_lib:dir(), "LATEST.LOG"))), + + %% Cleanup file:delete(File), ?verify_mnesia(Nodes, []), ?cleanup(1, Config), @@ -609,13 +614,6 @@ dump_log_load_regulation(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -ignore_fallback_at_startup(doc) -> - ["Start Mnesia without rollback of the database to the fallback. ", - "Once Mnesia has been (re)started the installed fallback should", - "be handled as a normal active fallback.", - "Install a customized event module which disables the termination", - "of Mnesia when mnesia_down occurrs with an active fallback."]. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% max_wait_for_decision(doc) -> diff --git a/lib/mnesia/test/mnesia_evil_backup.erl b/lib/mnesia/test/mnesia_evil_backup.erl index 9e0a8db1ae..68efa3f6ea 100644 --- a/lib/mnesia/test/mnesia_evil_backup.erl +++ b/lib/mnesia/test/mnesia_evil_backup.erl @@ -142,6 +142,9 @@ restore_errors(Config) when is_list(Config) -> ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{skip_tables, xxx}])), ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{recreate_tables, [schema]}])), ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{default_op, asdklasd}])), + MnesiaDir = mnesia_lib:dir(), + ?match({aborted, {not_a_log_file, _}}, mnesia:restore(filename:join(MnesiaDir, "schema.DAT"), [])), + ?match({aborted, _}, mnesia:restore(filename:join(MnesiaDir, "LATEST.LOG"), [])), ok. restore_clear(suite) -> []; @@ -488,6 +491,14 @@ install_fallback(Config) when is_list(Config) -> mnesia_test_lib:kill_mnesia([Node1, Node2]), timer:sleep(timer:seconds(1)), % Let it die! + ok = mnesia:start([{ignore_fallback_at_startup, true}]), + ok = mnesia:wait_for_tables([Tab, Tab2, Tab3], 10000), + ?match([{Tab, 6, test_nok}], mnesia:dirty_read({Tab, 6})), + mnesia_test_lib:kill_mnesia([Node1]), + application:set_env(mnesia, ignore_fallback_at_startup, false), + + timer:sleep(timer:seconds(1)), % Let it die! + ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab, Tab2, Tab3])), % Verify @@ -510,6 +521,13 @@ install_fallback(Config) when is_list(Config) -> file:delete(File3), ?match({error, _}, mnesia:install_fallback(File3)), ?match({error, _}, mnesia:install_fallback(File2, mnesia_badmod)), + ?match({error, _}, mnesia:install_fallback(File2, {foo, foo})), + ?match({error, _}, mnesia:install_fallback(File2, [{foo, foo}])), + ?match({error, {badarg, {skip_tables, _}}}, + mnesia:install_fallback(File2, [{default_op, skip_tables}, + {default_op, keep_tables}, + {keep_tables, [Tab, Tab2, Tab3]}, + {skip_tables, [foo,{asd}]}])), ?match(ok, mnesia:install_fallback(File2, mnesia_backup)), ?match(ok, file:delete(File)), ?match(ok, file:delete(File2)), @@ -535,6 +553,7 @@ uninstall_fallback(Config) when is_list(Config) -> ?match(ok, mnesia:install_fallback(File2)), ?match(ok, file:delete(File)), ?match(ok, file:delete(File2)), + ?match({error, _}, mnesia:uninstall_fallback([foobar])), ?match(ok, mnesia:uninstall_fallback()), mnesia_test_lib:kill_mnesia([Node1, Node2]), diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile index c120865213..a42967644a 100644 --- a/lib/observer/src/Makefile +++ b/lib/observer/src/Makefile @@ -61,6 +61,7 @@ MODULES= \ etop_txt \ observer \ observer_app_wx \ + observer_alloc_wx \ observer_html_lib \ observer_lib \ observer_perf_wx \ diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src index 197d0a2a95..e293990d64 100644 --- a/lib/observer/src/observer.app.src +++ b/lib/observer/src/observer.app.src @@ -44,6 +44,7 @@ etop_tr, etop_txt, observer, + observer_alloc_wx, observer_app_wx, observer_html_lib, observer_lib, diff --git a/lib/observer/src/observer_alloc_wx.erl b/lib/observer/src/observer_alloc_wx.erl new file mode 100644 index 0000000000..0c4bc9ee4b --- /dev/null +++ b/lib/observer/src/observer_alloc_wx.erl @@ -0,0 +1,256 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +-module(observer_alloc_wx). + +-export([start_link/2]). + +%% wx_object callbacks +-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, + handle_event/2, handle_sync_event/3, handle_cast/2]). + +-behaviour(wx_object). +-include_lib("wx/include/wx.hrl"). +-include("observer_defs.hrl"). + +-record(state, + { + offset = 0.0, + active = false, + parent, + windows, + data = {0, queue:new()}, + panel, + paint, + appmon, + async + }). + +-define(ALLOC_W, 1). +-define(UTIL_W, 2). + +start_link(Notebook, Parent) -> + wx_object:start_link(?MODULE, [Notebook, Parent], []). + +init([Notebook, Parent]) -> + try + Panel = wxPanel:new(Notebook), + Main = wxBoxSizer:new(?wxVERTICAL), + Style = ?wxFULL_REPAINT_ON_RESIZE bor ?wxCLIP_CHILDREN, + Carrier = wxPanel:new(Panel, [{winid, ?ALLOC_W}, {style,Style}]), + Utilz = wxPanel:new(Panel, [{winid, ?UTIL_W}, {style,Style}]), + BorderFlags = ?wxLEFT bor ?wxRIGHT, + wxSizer:add(Main, Carrier, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP}, + {proportion, 1}, {border, 5}]), + + wxSizer:add(Main, Utilz, [{flag, ?wxEXPAND bor BorderFlags}, + {proportion, 1}, {border, 5}]), + + MemWin = {MemPanel,_} = create_mem_info(Panel), + wxSizer:add(Main, MemPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxBOTTOM}, + {proportion, 1}, {border, 5}]), + wxWindow:setSizer(Panel, Main), + + PaintInfo = observer_perf_wx:setup_graph_drawing([Carrier, Utilz]), + {Panel, #state{parent=Parent, + panel =Panel, + windows = {Carrier, Utilz, MemWin}, + paint=PaintInfo} + } + catch _:Err -> + io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]), + {stop, Err} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +handle_event(#wx{event=#wxCommand{type=command_menu_selected}}, + State = #state{}) -> + {noreply, State}; + +handle_event(Event, _State) -> + error({unhandled_event, Event}). + +%%%%%%%%%% +handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_, + #state{active=Active, offset=Offset, paint=Paint, + windows=Windows, data=Data}) -> + %% Sigh workaround bug on MacOSX (Id in paint event is always 0) + Id = if Panel =:= element(?ALLOC_W, Windows) -> alloc; + Panel =:= element(?UTIL_W, Windows) -> utilz + end, + observer_perf_wx:refresh_panel(Panel, Id, Offset, Data, Active, Paint), + ok. +%%%%%%%%%% +handle_call(Event, From, _State) -> + error({unhandled_call, Event, From}). + +handle_cast(Event, _State) -> + error({unhandled_cast, Event}). +%%%%%%%%%% + +handle_info({Key, {promise_reply, {badrpc, _}}}, #state{async=Key} = State) -> + {noreply, State#state{active=false, appmon=undefined}}; + +handle_info({Key, {promise_reply, SysInfo}}, #state{async=Key, data=Data} = State) -> + Info = alloc_info(SysInfo), + update_alloc(State, Info), + {noreply, State#state{offset=0.0, data = add_data(Info, Data), async=undefined}}; + +handle_info({refresh, Seq, Freq, Node}, #state{panel=Panel, appmon=Node, async=Key} = State) -> + wxWindow:refresh(Panel), + Next = Seq+1, + if + Next > Freq, Key =:= undefined -> + erlang:send_after(trunc(1000 / Freq), self(), {refresh, 1, Freq, Node}), + Req = rpc:async_call(Node, observer_backend, sys_info, []), + {noreply, State#state{offset=Seq/Freq, async=Req}}; + true -> + erlang:send_after(trunc(1000 / Freq), self(), {refresh, Next, Freq, Node}), + {noreply, State#state{offset=Seq/Freq}} + end; +handle_info({refresh, _Seq, _Freq, _Node}, State) -> + {noreply, State}; + +handle_info({active, Node}, State = #state{parent=Parent, panel=Panel, appmon=Old}) -> + create_menus(Parent, []), + try + Node = Old, + wxWindow:refresh(Panel), + {noreply, State#state{active=true}} + catch _:_ -> + SysInfo = observer_wx:try_rpc(Node, observer_backend, sys_info, []), + Info = alloc_info(SysInfo), + Freq = 6, + erlang:send_after(trunc(1000 / Freq), self(), {refresh, 1, Freq, Node}), + wxWindow:refresh(Panel), + {noreply, State#state{active=true, appmon=Node, offset=0.0, + data = add_data(Info, {0, queue:new()})}} + end; + +handle_info(not_active, State = #state{appmon=_Pid}) -> + {noreply, State#state{active=false}}; + +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]), + {noreply, State}. + +terminate(_Event, #state{}) -> + ok. +code_change(_, _, State) -> + State. + +%%%%%%%%%% + +add_data(Stats, {N, Q}) when N > 60 -> + {N, queue:drop(queue:in(Stats, Q))}; +add_data(Stats, {N, Q}) -> + {N+1, queue:in(Stats, Q)}. + +update_alloc(#state{windows={_, _, {_, Grid}}}, Fields) -> + Max = wxListCtrl:getItemCount(Grid), + Update = fun({Name, BS, CS}, Row) -> + (Row >= Max) andalso wxListCtrl:insertItem(Grid, Row, ""), + wxListCtrl:setItem(Grid, Row, 0, observer_lib:to_str(Name)), + wxListCtrl:setItem(Grid, Row, 1, observer_lib:to_str(BS div 1024)), + wxListCtrl:setItem(Grid, Row, 2, observer_lib:to_str(CS div 1024)), + Row + 1 + end, + lists:foldl(Update, 0, Fields), + Fields. + +alloc_info(SysInfo) -> + AllocInfo = proplists:get_value(alloc_info, SysInfo, []), + alloc_info(AllocInfo, [], 0, 0, true). + +alloc_info([{Type,Instances}|Allocators],TypeAcc,TotalBS,TotalCS,IncludeTotal) -> + {BS,CS,NewTotalBS,NewTotalCS,NewIncludeTotal} = + sum_alloc_instances(Instances,0,0,TotalBS,TotalCS), + alloc_info(Allocators,[{Type,BS,CS}|TypeAcc],NewTotalBS,NewTotalCS, + IncludeTotal andalso NewIncludeTotal); +alloc_info([],TypeAcc,TotalBS,TotalCS,IncludeTotal) -> + Types = [X || X={_,BS,CS} <- TypeAcc, (BS>0 orelse CS>0)], + case IncludeTotal of + true -> + [{total,TotalBS,TotalCS} | lists:reverse(Types)]; + false -> + lists:reverse(Types) + end. + +sum_alloc_instances(false,BS,CS,TotalBS,TotalCS) -> + {BS,CS,TotalBS,TotalCS,false}; +sum_alloc_instances([{_,_,Data}|Instances],BS,CS,TotalBS,TotalCS) -> + {NewBS,NewCS,NewTotalBS,NewTotalCS} = + sum_alloc_one_instance(Data,BS,CS,TotalBS,TotalCS), + sum_alloc_instances(Instances,NewBS,NewCS,NewTotalBS,NewTotalCS); +sum_alloc_instances([],BS,CS,TotalBS,TotalCS) -> + {BS,CS,TotalBS,TotalCS,true}. + +sum_alloc_one_instance([{sbmbcs,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}| + Rest],OldBS,OldCS,TotalBS,TotalCS) -> + sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS,TotalCS); +sum_alloc_one_instance([{_,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}| + Rest],OldBS,OldCS,TotalBS,TotalCS) -> + sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS); +sum_alloc_one_instance([{_,[{blocks_size,BS},{carriers_size,CS}]}| + Rest],OldBS,OldCS,TotalBS,TotalCS) -> + sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS); +sum_alloc_one_instance([_|Rest],BS,CS,TotalBS,TotalCS) -> + sum_alloc_one_instance(Rest,BS,CS,TotalBS,TotalCS); +sum_alloc_one_instance([],BS,CS,TotalBS,TotalCS) -> + {BS,CS,TotalBS,TotalCS}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_mem_info(Parent) -> + Panel = wxPanel:new(Parent), + wxWindow:setBackgroundColour(Panel, {255,255,255}), + Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES bor ?wxLC_VRULES, + Grid = wxListCtrl:new(Panel, [{style, Style}]), + Li = wxListItem:new(), + AddListEntry = fun({Name, Align, DefSize}, Col) -> + wxListItem:setText(Li, Name), + wxListItem:setAlign(Li, Align), + wxListCtrl:insertColumn(Grid, Col, Li), + wxListCtrl:setColumnWidth(Grid, Col, DefSize), + Col + 1 + end, + ListItems = [{"Allocator Type", ?wxLIST_FORMAT_LEFT, 200}, + {"Block size (kB)", ?wxLIST_FORMAT_RIGHT, 150}, + {"Carrier size (kB)",?wxLIST_FORMAT_RIGHT, 150}], + lists:foldl(AddListEntry, 0, ListItems), + wxListItem:destroy(Li), + + Sizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT}, + {border, 5}, {proportion, 1}]), + wxWindow:setSizerAndFit(Panel, Sizer), + {Panel, Grid}. + + +create_menus(Parent, _) -> + MenuEntries = + [{"File", + [ + ]} + ], + observer_wx:create_menus(Parent, MenuEntries). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl index 34c7b127ff..9592ab5977 100644 --- a/lib/observer/src/observer_lib.erl +++ b/lib/observer/src/observer_lib.erl @@ -493,8 +493,11 @@ link_entry2(Panel,{Target,Str},Cursor) -> wxWindow:setToolTip(TC, ToolTip), TC. -to_link(Tuple = {_Target, _Str}) -> - Tuple; +to_link(RegName={Name, Node}) when is_atom(Name), is_atom(Node) -> + Str = io_lib:format("{~p,~p}", [Name, Node]), + {RegName, Str}; +to_link(TI = {_Target, _Identifier}) -> + TI; to_link(Target0) -> Target=to_str(Target0), {Target, Target}. diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 8173349ed7..4df9218087 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -24,7 +24,8 @@ handle_event/2, handle_sync_event/3, handle_cast/2]). %% Drawing wrappers for DC and GC areas --export([haveGC/0, +-export([setup_graph_drawing/1, refresh_panel/6, + haveGC/0, setPen/2, setFont/3, setBrush/2, strokeLine/5, strokeLines/2, drawRoundedRectangle/6, drawText/4, getTextExtent/2]). @@ -42,13 +43,12 @@ data = {0, queue:new()}, panel, paint, - appmon, - usegc = false + appmon }). -define(wxGC, wxGraphicsContext). --record(paint, {font, small, pen, pen2, pens}). +-record(paint, {font, small, pen, pen2, pens, usegc = false}). -define(RQ_W, 1). -define(MEM_W, 2). @@ -63,14 +63,11 @@ init([Notebook, Parent]) -> Main = wxBoxSizer:new(?wxVERTICAL), Style = ?wxFULL_REPAINT_ON_RESIZE bor ?wxCLIP_CHILDREN, CPU = wxPanel:new(Panel, [{winid, ?RQ_W}, {style,Style}]), - wxWindow:setBackgroundColour(CPU, ?wxWHITE), wxSizer:add(Main, CPU, [{flag, ?wxEXPAND bor ?wxALL}, {proportion, 1}, {border, 5}]), MemIO = wxBoxSizer:new(?wxHORIZONTAL), MEM = wxPanel:new(Panel, [{winid, ?MEM_W}, {style,Style}]), - wxWindow:setBackgroundColour(MEM, ?wxWHITE), IO = wxPanel:new(Panel, [{winid, ?IO_W}, {style,Style}]), - wxWindow:setBackgroundColour(IO, ?wxWHITE), wxSizer:add(MemIO, MEM, [{flag, ?wxEXPAND bor ?wxLEFT}, {proportion, 1}, {border, 5}]), wxSizer:add(MemIO, IO, [{flag, ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT}, @@ -79,53 +76,56 @@ init([Notebook, Parent]) -> {proportion, 1}, {border, 5}]), wxWindow:setSizer(Panel, Main), - wxPanel:connect(CPU, paint, [callback]), - wxPanel:connect(IO, paint, [callback]), - wxPanel:connect(MEM, paint, [callback]), - case os:type() of - {win32, _} -> %% Ignore erase on windows - wxPanel:connect(CPU, erase_background, [{callback, fun(_,_) -> ok end}]), - wxPanel:connect(IO, erase_background, [{callback, fun(_,_) -> ok end}]), - wxPanel:connect(MEM, erase_background, [{callback, fun(_,_) -> ok end}]); - _ -> ok - end, + PaintInfo = setup_graph_drawing([CPU, MEM, IO]), + process_flag(trap_exit, true), + {Panel, #state{parent=Parent, + panel =Panel, + windows = {CPU, MEM, IO}, + paint=PaintInfo + }} + catch _:Err -> + io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]), + {stop, Err} + end. + +setup_graph_drawing(Panels) -> + IsWindows = element(1, os:type()) =:= win32, + IgnoreCB = {callback, fun(_,_) -> ok end}, + Do = fun(Panel) -> + wxWindow:setBackgroundColour(Panel, ?wxWHITE), + wxPanel:connect(Panel, paint, [callback]), + IsWindows andalso + wxPanel:connect(Panel, erase_background, [IgnoreCB]) + end, + _ = [Do(Panel) || Panel <- Panels], UseGC = haveGC(), Version28 = ?wxMAJOR_VERSION =:= 2 andalso ?wxMINOR_VERSION =:= 8, {Font, SmallFont} - = case os:type() of - {unix, _} when UseGC, Version28 -> + = if UseGC, Version28 -> %% Def font is really small when using Graphics contexts in 2.8 %% Hardcode it F = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD), SF = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), {F, SF}; - _ -> + true -> DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), DefSize = wxFont:getPointSize(DefFont), DefFamily = wxFont:getFamily(DefFont), - F = wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), - SF = wxFont:new(DefSize-1, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + F = wxFont:new(DefSize-1, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), + SF = wxFont:new(DefSize-2, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), {F, SF} end, BlackPen = wxPen:new({0,0,0}, [{width, 2}]), - Pens = [wxPen:new(Col, [{width, 2}]) || Col <- tuple_to_list(colors())], - process_flag(trap_exit, true), - {Panel, #state{parent=Parent, - panel =Panel, - windows = {CPU, MEM, IO}, - usegc=UseGC, - paint=#paint{font = Font, - small = SmallFont, - pen = ?wxGREY_PEN, - pen2 = BlackPen, - pens = list_to_tuple(Pens) - } - }} - catch _:Err -> - io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]), - {stop, Err} - end. + Pens = [wxPen:new(Col, [{width, 3}]) || Col <- tuple_to_list(colors())], + #paint{usegc = UseGC, + font = Font, + small = SmallFont, + pen = ?wxGREY_PEN, + pen2 = BlackPen, + pens = list_to_tuple(Pens) + }. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -139,21 +139,25 @@ handle_event(Event, _State) -> %%%%%%%%%% handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_, #state{active=Active, offset=Offset, paint=Paint, - windows=Windows, data=Data, usegc=UseGC}) -> - %% PaintDC must be created in a callback to work on windows. + windows=Windows, data=Data}) -> %% Sigh workaround bug on MacOSX (Id in paint event is always 0) %% Panel = element(Id, Windows), - Id = if Panel =:= element(?RQ_W, Windows) -> ?RQ_W; - Panel =:= element(?MEM_W, Windows) -> ?MEM_W; - Panel =:= element(?IO_W, Windows) -> ?IO_W + Id = if Panel =:= element(?RQ_W, Windows) -> runq; + Panel =:= element(?MEM_W, Windows) -> memory; + Panel =:= element(?IO_W, Windows) -> io end, - IsWindows = element(1, os:type()) =:= win32, - DC = if IsWindows -> + refresh_panel(Panel, Id, Offset, Data, Active, Paint), + ok. + +refresh_panel(Panel, Id, Offset, Data, Active, #paint{usegc=UseGC} = Paint) -> + %% PaintDC must be created in a callback to work on windows. + IsWindows = element(1, os:type()) =:= win32, + DC = if IsWindows -> %% Ugly hack to aviod flickering on windows, works on windows only %% But the other platforms are doublebuffered by default wx:typeCast(wxBufferedPaintDC:new(Panel), wxPaintDC); - true -> + true -> wxPaintDC:new(Panel) end, IsWindows andalso wxDC:clear(DC), @@ -167,8 +171,9 @@ handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_, io:format("Internal error ~p ~p~n",[Err, erlang:get_stacktrace()]) end, UseGC andalso ?wxGC:destroy(GC), - wxPaintDC:destroy(DC), - ok. + wxPaintDC:destroy(DC). + + %%%%%%%%%% handle_call(Event, From, _State) -> error({unhandled_call, Event, From}). @@ -247,10 +252,10 @@ create_menus(Parent, _) -> observer_wx:create_menus(Parent, MenuEntries). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -collect_data(?RQ_W, {N, Q}) -> +collect_data(runq, {N, Q}) -> case queue:to_list(Q) of - [] -> {0, 0, []}; - [_] -> {0, 0, []}; + [] -> {0, 0, [], []}; + [_] -> {0, 0, [], []}; [{stats, _Ver, Init0, _IO, _Mem}|Data0] -> Init = lists:sort(Init0), [_|Data=[First|_]] = lists:foldl(fun({stats, _, T0, _, _}, [Prev|Acc]) -> @@ -258,25 +263,46 @@ collect_data(?RQ_W, {N, Q}) -> Delta = calc_delta(TN, Prev), [TN, list_to_tuple(Delta)|Acc] end, [Init], Data0), - {N, lmax(Data), lists:reverse([First|Data])} + NoGraphs = tuple_size(First), + {N, lmax(Data), lists:reverse([First|Data]), lists:seq(1, NoGraphs)} end; -collect_data(?MEM_W, {N, Q}) -> +collect_data(memory, {N, Q}) -> MemT = mem_types(), Data = [list_to_tuple([Value || {Type,Value} <- MemInfo, lists:member(Type, MemT)]) || {stats, _Ver, _RQ, _IO, MemInfo} <- queue:to_list(Q)], - {N, lmax(Data), Data}; -collect_data(?IO_W, {N, Q}) -> + {N, lmax(Data), Data, MemT}; +collect_data(io, {N, Q}) -> case queue:to_list(Q) of - [] -> {0, 0, []}; - [_] -> {0, 0, []}; + [] -> {0, 0, [], []}; + [_] -> {0, 0, [], []}; [{stats, _Ver, _RQ, {{_,In0}, {_,Out0}}, _Mem}|Data0] -> [_,_|Data=[First|_]] = lists:foldl(fun({stats, _, _, {{_,In}, {_,Out}}, _}, [PIn,Pout|Acc]) -> [In,Out,{In-PIn,Out-Pout}|Acc] end, [In0,Out0], Data0), - {N, lmax(Data), lists:reverse([First|Data])} - end. + {N, lmax(Data), lists:reverse([First|Data]), [input, output]} + end; +collect_data(alloc, {N, Q}) -> + List = queue:to_list(Q), + Data = [list_to_tuple([Carrier || {_Type,_Block,Carrier} <- MemInfo]) + || MemInfo <- List], + Info = case List of %% Varies depending on erlang build config/platform + [MInfo|_] -> [Type || {Type, _, _} <- MInfo]; + _ -> [] + end, + {N, lmax(Data), Data, Info}; + +collect_data(utilz, {N, Q}) -> + List = queue:to_list(Q), + Data = [list_to_tuple([round(100*Block/Carrier) || {_Type,Block,Carrier} <- MemInfo]) + || MemInfo <- List], + Info = case List of %% Varies depending on erlang build config/platform + [MInfo|_] -> [Type || {Type, _, _} <- MInfo]; + _ -> [] + end, + {N, lmax(Data), Data, Info}. + mem_types() -> [total, processes, atom, binary, code, ets]. @@ -299,14 +325,14 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) %% This can be optimized a lot by collecting data once %% and draw to memory and then blit memory and only draw new entries in new memory %% area. Hmm now rewritten to use ?wxGC I don't now if it is feasable. - {Len, Max0, Hs} = collect_data(Id, Data), - Max = calc_max(Max0), - NoGraphs = try tuple_size(hd(Hs)) catch _:_ -> 0 end, + {Len, Max0, Hs, Info} = collect_data(Id, Data), + {Max,_,_} = MaxDisp = calc_max(Id, Max0), Size = wxWindow:getClientSize(Panel), - {X0,Y0,WS,HS} = draw_borders(Id, NoGraphs, DC, Size, Max, Paint), + {X0,Y0,WS,HS, DrawBs} = draw_borders(Id, Info, DC, Size, MaxDisp, Paint), Last = 60*WS+X0-1, Start = max(61-Len, 0)*WS+X0 - Offset*WS, Samples = length(Hs), + NoGraphs = try tuple_size(hd(Hs)) catch _:_ -> 0 end, case Active andalso Samples > 1 andalso NoGraphs > 0 of true -> Draw = fun(N) -> @@ -315,14 +341,16 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) strokeLines(DC, Lines), N+1 end, - [Draw(I) || I <- lists:seq(NoGraphs, 1, -1)]; + [Draw(I) || I <- lists:seq(NoGraphs, 1, -1)], + DrawBs(); false -> - Info = case Active andalso Samples =< 1 of - true -> "Waiting on data"; + DrawBs(), + Text = case Active andalso Samples =< 1 of + true -> "Waiting for data"; false -> "Information not available" end, setFont(DC, Small, {0,0,0}), - drawText(DC, Info, X0 + 100, element(2,Size) div 2) + drawText(DC, Text, X0 + 100, element(2,Size) div 2) end, ok. @@ -397,9 +425,8 @@ spline_tan(Y0, Y1, Y2, Y3) -> -define(BW, 5). -define(BH, 5). -draw_borders(Type, NoGraphs, DC, {W,H}, Max, +draw_borders(Type, Info, DC, {W,H}, {Max, Unit, MaxUnit}, #paint{pen=Pen, pen2=Pen2, font=Font, small=Small}) -> - {Unit, MaxUnit} = bytes(Type, Max), Str1 = observer_lib:to_str(MaxUnit), Str2 = observer_lib:to_str(MaxUnit div 2), Str3 = observer_lib:to_str(0), @@ -410,10 +437,10 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, GraphX0 = ?BW+TW+?BW, GraphX1 = W-?BW*4, - TopTextX = ?BW+TW+?BW, - MaxTextY = ?BH+TH+?BH, + TopTextX = ?BW*3+TW, + MaxTextY = TH+?BH, BottomTextY = H-?BH-TH, - SecondsY = BottomTextY - ?BH - TH, + SecondsY = BottomTextY - TH, GraphY0 = MaxTextY + (TH / 2), GraphY1 = SecondsY - ?BH, GraphW = GraphX1-GraphX0-1, @@ -447,17 +474,7 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, strokeLine(DC, GraphX0-3, GraphY50, GraphX1, GraphY50), strokeLine(DC, GraphX0-3, GraphY75, GraphX1, GraphY75), - setPen(DC, Pen2), - strokeLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1}, - {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1}, - {GraphX0, GraphY0-1}]), - setFont(DC, Font, {0,0,0}), - case Type of - ?RQ_W -> drawText(DC, "Scheduler Utilization (%) ", TopTextX,?BH); - ?MEM_W -> drawText(DC, "Memory Usage " ++ Unit, TopTextX,?BH); - ?IO_W -> drawText(DC, "IO Usage " ++ Unit, TopTextX,?BH) - end, Text = fun(X,Y, Str, PenId) -> if PenId == 0 -> @@ -468,32 +485,65 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, end, drawText(DC, Str, X, Y), {StrW, _} = getTextExtent(DC, Str), - StrW + X + SpaceW + StrW + X + ?BW*2 end, + case Type of - ?RQ_W -> - TN0 = Text(?BW, BottomTextY, "Scheduler: ", 0), + runq -> + drawText(DC, "Scheduler Utilization (%) ", TopTextX, ?BH), + TN0 = Text(TopTextX, BottomTextY, "Scheduler: ", 0), lists:foldl(fun(Id, Pos0) -> Text(Pos0, BottomTextY, integer_to_list(Id), Id) - end, TN0, lists:seq(1, NoGraphs)); - ?MEM_W -> + end, TN0, Info); + memory -> + drawText(DC, "Memory Usage " ++ Unit, TopTextX,?BH), + lists:foldl(fun(MType, {PenId, Pos0}) -> + Str = to_string(MType), + Pos = Text(Pos0, BottomTextY, Str, PenId), + {PenId+1, Pos} + end, {1, TopTextX}, Info); + io -> + drawText(DC, "IO Usage " ++ Unit, TopTextX,?BH), + lists:foldl(fun(MType, {PenId, Pos0}) -> + Str = to_string(MType), + Pos = Text(Pos0, BottomTextY, Str, PenId), + {PenId+1, Pos} + end, {1, TopTextX}, Info); + alloc -> + drawText(DC, "Carrier Size " ++ Unit, TopTextX,?BH); + utilz -> + drawText(DC, "Carrier Utilization (%)" ++ Unit, TopTextX,?BH), lists:foldl(fun(MType, {PenId, Pos0}) -> - Str = uppercase(atom_to_list(MType)), + Str = to_string(MType), Pos = Text(Pos0, BottomTextY, Str, PenId), {PenId+1, Pos} - end, {1, ?BW}, mem_types()); - ?IO_W -> - TN0 = Text(?BW, BottomTextY, "Input", 1), - Text(TN0, BottomTextY, "Output", 2) + end, {1, TopTextX}, Info) end, - {GraphX0+1, GraphY1, ScaleW, ScaleH}. + DrawBorder = fun() -> + setPen(DC, Pen2), + strokeLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1}, + {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1}, + {GraphX0, GraphY0-1}]) + end, + {GraphX0+1, GraphY1, ScaleW, ScaleH, DrawBorder}. + +to_string(Atom) -> + Name = atom_to_list(Atom), + case lists:reverse(Name) of + "colla_" ++ Rev -> + uppercase(lists:reverse(Rev)); + _ -> + uppercase(Name) + end. uppercase([C|Rest]) -> [C-$a+$A|Rest]. -calc_max(Max) when Max < 10 -> 10; -calc_max(Max) -> calc_max1(Max). +calc_max(Type, Max) -> + bytes(Type, Max). +calc_max1(Max) when Max < 10 -> + 10; calc_max1(Max) -> case Max div 10 of X when X < 10 -> @@ -506,23 +556,36 @@ calc_max1(Max) -> 10*calc_max1(X) end. -bytes(?RQ_W, Val) -> {"", Val}; +bytes(runq, Val) -> + Upper = calc_max1(Val), + {Upper, "", Upper}; +bytes(utilz, Val) -> + Upper = calc_max1(Val), + {Upper, "", Upper}; bytes(_, B) -> KB = B div 1024, MB = KB div 1024, GB = MB div 1024, if - GB > 10 -> {"(GB)", GB}; - MB > 10 -> {"(MB)", MB}; - KB > 0 -> {"(KB)", KB}; - true -> {"(B)", B} + GB > 10 -> + Upper = calc_max1(GB), + {Upper*1024*1024*1024, "(GB)", Upper}; + MB > 10 -> + Upper = calc_max1(MB), + {Upper*1024*1024, "(MB)", Upper}; + KB > 0 -> + Upper = calc_max1(KB), + {Upper*1024, "(KB)", Upper}; + true -> + Upper = calc_max1(B), + {Upper, "(B)", Upper} end. colors() -> - {{200, 50, 50}, {50, 200, 50}, {50, 50, 200}, - {255, 110, 0}, {50, 200, 200}, {200, 50, 200}, - {240, 200, 80}, {140, 2, 140}, - {100, 200, 240}, {100, 240, 100} + {{240, 100, 100}, {100, 240, 100}, {100, 100, 240}, + {220, 220, 80}, {100, 240, 240}, {240, 100, 240}, + {100, 25, 25}, {25, 100, 25}, {25, 25, 100}, + {120, 120, 0}, {25, 100, 100}, {100, 50, 100} }. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl index f989f9cf97..ea89590e84 100644 --- a/lib/observer/src/observer_sys_wx.erl +++ b/lib/observer/src/observer_sys_wx.erl @@ -37,7 +37,6 @@ parent_notebook, panel, sizer, menubar, - alloc, fields, timer}). @@ -48,7 +47,6 @@ start_link(Notebook, Parent) -> init([Notebook, Parent]) -> SysInfo = observer_backend:sys_info(), - AllocInfo = proplists:get_value(alloc_info, SysInfo, []), {Info, Stat} = info_fields(), Panel = wxPanel:new(Notebook), Sizer = wxBoxSizer:new(?wxVERTICAL), @@ -60,16 +58,13 @@ init([Notebook, Parent]) -> wxSizer:add(TopSizer, FPanel0, [{flag, ?wxEXPAND}, {proportion, 1}]), wxSizer:add(TopSizer, FPanel1, [{flag, ?wxEXPAND}, {proportion, 1}]), BorderFlags = ?wxLEFT bor ?wxRIGHT, - {MemPanel, MemoryInfo} = create_mem_info(Panel, AllocInfo), wxSizer:add(Sizer, TopSizer, [{flag, ?wxEXPAND bor BorderFlags bor ?wxTOP}, {proportion, 0}, {border, 5}]), - wxSizer:add(Sizer, MemPanel, [{flag, ?wxEXPAND bor BorderFlags bor ?wxBOTTOM}, - {proportion, 1}, {border, 5}]), wxPanel:setSizer(Panel, Sizer), Timer = observer_lib:start_timer(10), {Panel, #sys_wx_state{parent=Parent, parent_notebook=Notebook, - panel=Panel, sizer=Sizer, alloc=MemoryInfo, + panel=Panel, sizer=Sizer, timer=Timer, fields=Fields0 ++ Fields1}}. create_sys_menu(Parent) -> @@ -77,91 +72,13 @@ create_sys_menu(Parent) -> #create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh interval"}]}, observer_wx:create_menus(Parent, [View]). -update_syspage(#sys_wx_state{node = Node, fields=Fields, sizer=Sizer, alloc=AllocCtrl}) -> +update_syspage(#sys_wx_state{node = Node, fields=Fields, sizer=Sizer}) -> SysInfo = observer_wx:try_rpc(Node, observer_backend, sys_info, []), - AllocInfo = proplists:get_value(alloc_info, SysInfo, []), {Info, Stat} = info_fields(), observer_lib:update_info(Fields, observer_lib:fill_info(Info, SysInfo) ++ observer_lib:fill_info(Stat, SysInfo)), - update_alloc(AllocCtrl, AllocInfo), wxSizer:layout(Sizer). -create_mem_info(Parent, Fields) -> - Panel = wxPanel:new(Parent), - wxWindow:setBackgroundColour(Panel, {255,255,255}), - Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES bor ?wxLC_VRULES, - Grid = wxListCtrl:new(Panel, [{style, Style}]), - Li = wxListItem:new(), - AddListEntry = fun({Name, Align, DefSize}, Col) -> - wxListItem:setText(Li, Name), - wxListItem:setAlign(Li, Align), - wxListCtrl:insertColumn(Grid, Col, Li), - wxListCtrl:setColumnWidth(Grid, Col, DefSize), - Col + 1 - end, - ListItems = [{"Allocator Type", ?wxLIST_FORMAT_LEFT, 200}, - {"Block size (kB)", ?wxLIST_FORMAT_RIGHT, 150}, - {"Carrier size (kB)",?wxLIST_FORMAT_RIGHT, 150}], - lists:foldl(AddListEntry, 0, ListItems), - wxListItem:destroy(Li), - update_alloc(Grid, Fields), - - Sizer = wxBoxSizer:new(?wxVERTICAL), - wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxLEFT bor ?wxRIGHT}, - {border, 5}, {proportion, 1}]), - wxWindow:setSizerAndFit(Panel, Sizer), - {Panel, Grid}. - -update_alloc(Grid, AllocInfo) -> - Fields = alloc_info(AllocInfo, [], 0, 0, true), - wxListCtrl:deleteAllItems(Grid), - Update = fun({Name, BS, CS}, Row) -> - wxListCtrl:insertItem(Grid, Row, ""), - wxListCtrl:setItem(Grid, Row, 0, observer_lib:to_str(Name)), - wxListCtrl:setItem(Grid, Row, 1, observer_lib:to_str(BS div 1024)), - wxListCtrl:setItem(Grid, Row, 2, observer_lib:to_str(CS div 1024)), - Row + 1 - end, - lists:foldl(Update, 0, Fields), - Fields. - -alloc_info([{Type,Instances}|Allocators],TypeAcc,TotalBS,TotalCS,IncludeTotal) -> - {BS,CS,NewTotalBS,NewTotalCS,NewIncludeTotal} = - sum_alloc_instances(Instances,0,0,TotalBS,TotalCS), - alloc_info(Allocators,[{Type,BS,CS}|TypeAcc],NewTotalBS,NewTotalCS, - IncludeTotal andalso NewIncludeTotal); -alloc_info([],TypeAcc,TotalBS,TotalCS,IncludeTotal) -> - Types = [X || X={_,BS,CS} <- TypeAcc, (BS>0 orelse CS>0)], - case IncludeTotal of - true -> - [{total,TotalBS,TotalCS} | lists:reverse(Types)]; - false -> - lists:reverse(Types) - end. - -sum_alloc_instances(false,BS,CS,TotalBS,TotalCS) -> - {BS,CS,TotalBS,TotalCS,false}; -sum_alloc_instances([{_,_,Data}|Instances],BS,CS,TotalBS,TotalCS) -> - {NewBS,NewCS,NewTotalBS,NewTotalCS} = - sum_alloc_one_instance(Data,BS,CS,TotalBS,TotalCS), - sum_alloc_instances(Instances,NewBS,NewCS,NewTotalBS,NewTotalCS); -sum_alloc_instances([],BS,CS,TotalBS,TotalCS) -> - {BS,CS,TotalBS,TotalCS,true}. - -sum_alloc_one_instance([{sbmbcs,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}| - Rest],OldBS,OldCS,TotalBS,TotalCS) -> - sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS,TotalCS); -sum_alloc_one_instance([{_,[{blocks_size,BS,_,_},{carriers_size,CS,_,_}]}| - Rest],OldBS,OldCS,TotalBS,TotalCS) -> - sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS); -sum_alloc_one_instance([{_,[{blocks_size,BS},{carriers_size,CS}]}| - Rest],OldBS,OldCS,TotalBS,TotalCS) -> - sum_alloc_one_instance(Rest,OldBS+BS,OldCS+CS,TotalBS+BS,TotalCS+CS); -sum_alloc_one_instance([_|Rest],BS,CS,TotalBS,TotalCS) -> - sum_alloc_one_instance(Rest,BS,CS,TotalBS,TotalCS); -sum_alloc_one_instance([],BS,CS,TotalBS,TotalCS) -> - {BS,CS,TotalBS,TotalCS}. - info_fields() -> Info = [{"System and Architecture", [{"System Version", otp_release}, diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index 54c4092a78..cf602569aa 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -43,6 +43,7 @@ -define(LAST_NODES_MENU_ID, 2000). -define(TRACE_STR, "Trace Overview"). +-define(ALLOC_STR, "Memory Allocators"). %% Records -record(state, @@ -58,6 +59,7 @@ trace_panel, app_panel, perf_panel, + allc_panel, active_tab, node, nodes, @@ -149,6 +151,10 @@ setup(#state{frame = Frame} = State) -> PerfPanel = observer_perf_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, PerfPanel, "Load Charts", []), + %% Memory Allocator Viewer Panel + AllcPanel = observer_alloc_wx:start_link(Notebook, self()), + wxNotebook:addPage(Notebook, AllcPanel, ?ALLOC_STR, []), + %% App Viewer Panel AppPanel = observer_app_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, AppPanel, "Applications", []), @@ -184,6 +190,7 @@ setup(#state{frame = Frame} = State) -> trace_panel = TracePanel, app_panel = AppPanel, perf_panel = PerfPanel, + allc_panel = AllcPanel, active_tab = SysPid, node = node(), nodes = Nodes @@ -505,7 +512,7 @@ check_page_title(Notebook) -> get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys, tv_panel=Tv, trace_panel=Trace, app_panel=App, - perf_panel=Perf + perf_panel=Perf, allc_panel=Alloc }) -> Panel = case check_page_title(Notebook) of "Processes" -> Pro; @@ -513,13 +520,14 @@ get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys, "Table Viewer" -> Tv; ?TRACE_STR -> Trace; "Load Charts" -> Perf; - "Applications" -> App + "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}) -> + perf_panel=Perf, allc_panel=Alloc}) -> case Pid of Pro -> "Processes"; Sys -> "System"; @@ -527,6 +535,7 @@ pid2panel(Pid, #state{pro_panel=Pro, sys_panel=Sys, Trace -> ?TRACE_STR; Perf -> "Load Charts"; App -> "Applications"; + Alloc -> ?ALLOC_STR; _ -> "unknown" end. @@ -617,8 +626,8 @@ default_menus(NodesMenuItems) -> %% automagicly, so just add them to a menu that always exist. %% But not to the help menu for some reason - {Tag, Menus} = NodeMenu, - [{Tag, Menus ++ [Quit,About]}, LogMenu, {"&Help", [Help]}] + {Tag, Menus} = FileMenu, + [{Tag, Menus ++ [Quit,About]}, NodeMenu, LogMenu, {"&Help", [Help]}] end. clean_menus(Menus, MenuBar) -> diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 5900fd3ff3..ee87a8ddb2 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -17,9 +17,9 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"2\\.[1-2](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3 + [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3 {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], %% 17.0 %% Down to - max one major revision back - [{<<"2\\.[1-2](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3 + [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3 {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] %% 17.0 }. diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl index 107d064f4a..720526b3b9 100644 --- a/lib/wx/api_gen/wx_gen_cpp.erl +++ b/lib/wx/api_gen/wx_gen_cpp.erl @@ -975,6 +975,13 @@ build_ret(Name = "ev->m_scanCode",_,#type{base=bool,single=true,by_val=true}) -> w(" rt.addBool(~s);~n",[Name]), w("#else~n rt.addBool(false);~n",[]), w("#endif~n",[]); +build_ret(Name = "ev->m_metaDown",_,#type{base=bool,single=true,by_val=true}) -> + %% Hardcoded workaround for MAC on 2.9 and later + w("#if wxCHECK_VERSION(2,9,0) && defined(_MACOSX)~n", []), + w(" rt.addBool(ev->m_rawControlDown);~n",[]), + w("#else~n rt.addBool(~s);~n",[Name]), + w("#endif~n",[]); + build_ret(Name,_,#type{base=bool,single=true,by_val=true}) -> w(" rt.addBool(~s);~n",[Name]); build_ret(Name,{arg, both},#type{base=int,single=true,mod=M}) -> diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf index 2e961cce98..09f21af0f3 100644 --- a/lib/wx/api_gen/wxapi.conf +++ b/lib/wx/api_gen/wxapi.conf @@ -32,7 +32,10 @@ wxALWAYS_NATIVE_DOUBLE_BUFFER, wxGAUGE_EMULATE_INDETERMINATE_MODE, wxTR_DEFAULT_STYLE, - wxSL_LABELS + wxSL_LABELS, + wxCURSOR_DEFAULT, + wxCURSOR_ARROWWAIT, + wxCURSOR_MAX ]}. {gvars, diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp index 255b36c2fa..ae85931d8d 100644 --- a/lib/wx/c_src/gen/wxe_events.cpp +++ b/lib/wx/c_src/gen/wxe_events.cpp @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2014. All Rights Reserved. + * Copyright Ericsson AB 2008-2015. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -394,7 +394,11 @@ case 167: {// wxMouseEvent rt.addBool(ev->m_controlDown); rt.addBool(ev->m_shiftDown); rt.addBool(ev->m_altDown); +#if wxCHECK_VERSION(2,9,0) && defined(_MACOSX) + rt.addBool(ev->m_rawControlDown); +#else rt.addBool(ev->m_metaDown); +#endif rt.addInt(ev->m_wheelRotation); rt.addInt(ev->m_wheelDelta); rt.addInt(ev->m_linesPerAction); @@ -419,7 +423,11 @@ case 169: {// wxKeyEvent rt.addBool(ev->m_controlDown); rt.addBool(ev->m_shiftDown); rt.addBool(ev->m_altDown); +#if wxCHECK_VERSION(2,9,0) && defined(_MACOSX) + rt.addBool(ev->m_rawControlDown); +#else rt.addBool(ev->m_metaDown); +#endif #if !wxCHECK_VERSION(2,9,0) rt.addBool(ev->m_scanCode); #else diff --git a/lib/wx/c_src/gen/wxe_init.cpp b/lib/wx/c_src/gen/wxe_init.cpp index 3a4bced790..1673f2a1b3 100644 --- a/lib/wx/c_src/gen/wxe_init.cpp +++ b/lib/wx/c_src/gen/wxe_init.cpp @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2014. All Rights Reserved. + * Copyright Ericsson AB 2008-2015. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -56,6 +56,12 @@ void WxeApp::init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller) { rt.addTupleCount(2); rt.addAtom("wxMOD_CMD"); rt.addInt(wxMOD_CMD); rt.addTupleCount(2); + rt.addAtom("wxCURSOR_ARROWWAIT"); rt.addInt(wxCURSOR_ARROWWAIT); + rt.addTupleCount(2); + rt.addAtom("wxCURSOR_DEFAULT"); rt.addInt(wxCURSOR_DEFAULT); + rt.addTupleCount(2); + rt.addAtom("wxCURSOR_MAX"); rt.addInt(wxCURSOR_MAX); + rt.addTupleCount(2); rt.addAtom("wxBLACK"); rt.add(*(wxBLACK)); rt.addTupleCount(2); rt.addAtom("wxBLACK_BRUSH"); rt.addRef(getRef((void *)wxBLACK_BRUSH,memenv),"wxBrush"); @@ -138,7 +144,7 @@ void WxeApp::init_nonconsts(wxeMemEnv *memenv, ErlDrvTermData caller) { rt.addTupleCount(2); rt.addAtom("wxWHITE_PEN"); rt.addRef(getRef((void *)wxWHITE_PEN,memenv),"wxPen"); rt.addTupleCount(2); - rt.endList(57); + rt.endList(60); rt.addTupleCount(2); rt.send(); } diff --git a/lib/wx/configure.in b/lib/wx/configure.in index 4c4d4f41a8..fbdddb9220 100644 --- a/lib/wx/configure.in +++ b/lib/wx/configure.in @@ -441,19 +441,26 @@ else else CWXWIN_PROG=`cygpath -d "$PROGRAMFILES" | cygpath -f - 2>/dev/null` fi - CWXWIN3=$CWXWIN_PROG/wxWidgets-?.*.* - CWXWIN4=$CWXWIN_PROG/wxMSW-?.*.* - CWX_DOCUMENTED="/opt/local/pgm/wxMSW-?.*.* /opt/local/pgm/wxWidgets-?.*.*" + + CWXWIN3="$CWXWIN_PROG/wxWidgets-3.*.* $CWXWIN_PROG/wxWidgets-2.*.*" + CWXWIN4="$CWXWIN_PROG/wxMSW-3.*.* $CWXWIN_PROG/wxMSW-2.*.*" + + DOC_OPT=/opt/local/pgm + CWX_DOCUMENTED="$DOC_OPT/wxWidgets-2.*.* $DOC_OPT/wxMSW-2.*.*" + CWX_DOCUMENTED="$DOC_OPT/wxWidgets-3.*.* $DOC_OPT/wxMSW-3.*.* $CWX_DOCUMENTED" + case $ac_cv_sizeof_void_p in 8) - CWX_DOCUMENTED="/opt/local64/pgm/wxMSW-?.*.* /opt/local64/pgm/wxWidgets-?.*.* $CWX_DOCUMENTED" + DOC_OPT64=/opt/local64/pgm + CWX_DOCUMENTED="$DOC_OPT64/wxWidgets-2.*.* $DOC_OPT64/wxMSW-2.*.* $CWX_DOCUMENTED" + CWX_DOCUMENTED="$DOC_OPT64/wxWidgets-3.*.* $DOC_OPT64/wxMSW-3.*.* $CWX_DOCUMENTED" ;; *) true ;; - esac - - CWXPATH="$CWXWIN0 $CWXWIN1 $CWXWIN2 $CWX_DOCUMENTED $CWXWIN3.* $CWXWIN4.*" + esac + + CWXPATH="$CWXWIN0 $CWXWIN1 $CWXWIN2 $CWX_DOCUMENTED $CWXWIN3 $CWXWIN4" for dir in $CWXPATH; do AC_MSG_NOTICE(Checking: [$dir]) diff --git a/lib/wx/examples/demo/demo.erl b/lib/wx/examples/demo/demo.erl index 2f560096f5..65fb05cd94 100644 --- a/lib/wx/examples/demo/demo.erl +++ b/lib/wx/examples/demo/demo.erl @@ -256,9 +256,17 @@ handle_event(#wx{id = Id, wx_misc:launchDefaultBrowser("http://www.erlang.org/doc/apps/wx/part_frame.html"), {noreply, State}; ?wxID_ABOUT -> + WxWVer = io_lib:format("~p.~p.~p.~p", + [?wxMAJOR_VERSION, ?wxMINOR_VERSION, + ?wxRELEASE_NUMBER, ?wxSUBRELEASE_NUMBER]), + application:load(wx), + {ok, WxVsn} = application:get_key(wx, vsn), AboutString = "Demo of various widgets\n" - "Authors: Olle & Dan", + "Authors: Olle & Dan\n\n" ++ + "Frontend: wx-" ++ WxVsn ++ + "\nBackend: wxWidgets-" ++ lists:flatten(WxWVer), + wxMessageDialog:showModal(wxMessageDialog:new(State#state.win, AboutString, [{style, ?wxOK bor diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl index 348daf64ce..9b913c7c00 100644 --- a/lib/wx/include/wx.hrl +++ b/lib/wx/include/wx.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2014. All Rights Reserved. +%% Copyright Ericsson AB 2008-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1883,9 +1883,9 @@ -define(wxCURSOR_WAIT, 24). -define(wxCURSOR_WATCH, 25). -define(wxCURSOR_BLANK, 26). --define(wxCURSOR_DEFAULT, 27). --define(wxCURSOR_ARROWWAIT, 28). --define(wxCURSOR_MAX, 29). +-define(wxCURSOR_DEFAULT, wxe_util:get_const(wxCURSOR_DEFAULT)). +-define(wxCURSOR_ARROWWAIT, wxe_util:get_const(wxCURSOR_ARROWWAIT)). +-define(wxCURSOR_MAX, wxe_util:get_const(wxCURSOR_MAX)). % From "generic_2laywin.h" -define(wxLAYOUT_QUERY, 256). -define(wxLAYOUT_MRU_LENGTH, 16). |