From 9f0e2acbdc7105f02a8cac2aa11cf9259dca34ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 18 May 2017 23:05:02 +0200 Subject: Make lock counter info independent of the locks being counted This allows us to enable/disable lock counting at will, and greatly improves the performance of erts_debug:lock_counters/1 since we no longer have to worry about the lock counters "dying" while we're enumerating them. OTP-14412 --- lib/tools/doc/src/lcnt.xml | 8 -------- 1 file changed, 8 deletions(-) (limited to 'lib') diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml index 6e66a957ab..590049e681 100644 --- a/lib/tools/doc/src/lcnt.xml +++ b/lib/tools/doc/src/lcnt.xml @@ -109,14 +109,6 @@ statistics. If the server held any lock statistics data before the collect then that data is lost.

- -

- When collection occurs the runtime system transitions to a single thread, - blocking all other threads. No other tasks will be scheduled during this - operation. Depending on the size of the data this might take a long time - (several seconds) and cause timeouts in the system. -

-
-- cgit v1.2.3 From 392341efcebca6c41d4f88eb3f5731fe46473af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 23 May 2017 18:01:43 +0200 Subject: Enable lcnt smoke test on all builds that have lcnt enabled --- lib/tools/test/lcnt_SUITE.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl index 2e48b11740..a7da4b1844 100644 --- a/lib/tools/test/lcnt_SUITE.erl +++ b/lib/tools/test/lcnt_SUITE.erl @@ -151,10 +151,9 @@ t_swap_keys_file([File|Files]) -> %% Simple smoke test of actual lock-counting, if running on %% a run-time with lock-counting enabled. - smoke_lcnt(Config) -> - case erlang:system_info(build_type) of - lcnt -> + case catch erlang:system_info(lock_counting) of + true -> do_smoke_lcnt(Config); _ -> {skip,"Lock counting is not enabled"} -- cgit v1.2.3 From 33a1962290ba6a6116cefe9fabb2e6cef823b33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 23 Jun 2017 13:15:22 +0200 Subject: Break erts_debug:lock_counters/1 into separate BIFs --- lib/kernel/src/erts_debug.erl | 35 +++++++++++++++----- lib/kernel/src/kernel.app.src | 2 +- lib/tools/src/lcnt.erl | 77 +++++++++++++++++++++++++++++++------------ lib/tools/src/tools.app.src | 2 +- 4 files changed, 85 insertions(+), 31 deletions(-) (limited to 'lib') diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index ad92aafc2f..480db6814e 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -33,10 +33,10 @@ -export([breakpoint/2, disassemble/1, display/1, dist_ext_to_term/2, dump_monitors/1, dump_links/1, flat_size/1, - get_internal_state/1, instructions/0, lock_counters/1, + get_internal_state/1, instructions/0, map_info/1, same/2, set_internal_state/2, - size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, - dirty/3]). + size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, dirty/3, + lcnt_control/1, lcnt_control/2, lcnt_collect/0, lcnt_clear/0]). -spec breakpoint(MFA, Flag) -> non_neg_integer() when MFA :: {Module :: module(), @@ -142,12 +142,31 @@ ic(F) when is_function(F) -> io:format("Total: ~w~n",[lists:sum([C||{_I,C}<-Is])]), R. --spec lock_counters(info) -> term(); - (clear) -> ok; - ({copy_save, boolean()}) -> boolean(); - ({process_locks, boolean()}) -> boolean(). +-spec lcnt_control + (copy_save, boolean()) -> ok; + (mask, list(atom())) -> ok. -lock_counters(_) -> +lcnt_control(_Option, _Value) -> + erlang:nif_error(undef). + +-spec lcnt_control + (copy_save) -> boolean(); + (mask) -> list(atom()). + +lcnt_control(_Option) -> + erlang:nif_error(undef). + +-type lcnt_lock_info() :: {atom(), term(), atom(), term()}. + +-spec lcnt_collect() -> + list({duration, {non_neg_integer(), non_neg_integer()}} | + {locks, list(lcnt_lock_info())}). + +lcnt_collect() -> + erlang:nif_error(undef). + +-spec lcnt_clear() -> ok. +lcnt_clear() -> erlang:nif_error(undef). -spec same(Term1, Term2) -> boolean() when diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 1128ee3ec5..7aefbbd9b6 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -120,6 +120,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-9.0", "stdlib-3.0", "sasl-3.0"]} + {runtime_dependencies, ["erts-9.1", "stdlib-3.0", "sasl-3.0"]} ] }. diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl index d881fedbd5..139b3d8a4a 100644 --- a/lib/tools/src/lcnt.erl +++ b/lib/tools/src/lcnt.erl @@ -34,8 +34,11 @@ -export([start/0, stop/0]). -%% erts_debug:lock_counters api --export([rt_collect/0, +%% erts_debug:lcnt_xxx api +-export([rt_mask/0, + rt_mask/1, + rt_mask/2, + rt_collect/0, rt_collect/1, rt_clear/0, rt_clear/1, @@ -134,27 +137,61 @@ start_internal() -> %% -------------------------------------------------------------------- %% %% -%% API erts_debug:lock_counters +%% API erts_debug:lcnt_xxx %% %% -------------------------------------------------------------------- %% -rt_collect() -> - erts_debug:lock_counters(info). +rt_mask(Node, Categories) when is_atom(Node), is_list(Categories) -> + rpc:call(Node, lcnt, rt_mask, [Categories]). + +rt_mask(Node) when is_atom(Node) -> + rpc:call(Node, lcnt, rt_mask, []); + +rt_mask(Categories) when is_list(Categories) -> + case erts_debug:lcnt_control(copy_save) of + false -> + erts_debug:lcnt_control(mask, Categories); + true -> + {error, copy_save_enabled} + end. + +rt_mask() -> + erts_debug:lcnt_control(mask). rt_collect(Node) -> - rpc:call(Node, erts_debug, lock_counters, [info]). + rpc:call(Node, lcnt, rt_collect, []). +rt_collect() -> + erts_debug:lcnt_collect(). +rt_clear(Node) -> + rpc:call(Node, lcnt, rt_clear, []). rt_clear() -> - erts_debug:lock_counters(clear). + erts_debug:lcnt_clear(). -rt_clear(Node) -> - rpc:call(Node, erts_debug, lock_counters, [clear]). +rt_opt(Node, Arg) -> + rpc:call(Node, lcnt, rt_opt, [Arg]). -rt_opt({Type, Opt}) -> - erts_debug:lock_counters({Type, Opt}). +%% Compatibility shims for the "process/port_locks" options mentioned in the +%% manual. +rt_opt({process_locks, Enable}) -> + toggle_category(process, Enable); +rt_opt({port_locks, Enable}) -> + toggle_category(io, Enable); -rt_opt(Node, {Type, Opt}) -> - rpc:call(Node, erts_debug, lock_counters, [{Type, Opt}]). +rt_opt({Type, NewVal}) -> + PreviousVal = erts_debug:lcnt_control(Type), + erts_debug:lcnt_control(Type, NewVal), + PreviousVal. + +toggle_category(Category, true) -> + PreviousMask = erts_debug:lcnt_control(mask), + erts_debug:lcnt_control(mask, [Category | PreviousMask]), + lists:member(Category, PreviousMask); + +toggle_category(Category, false) -> + PreviousMask = erts_debug:lcnt_control(mask), + erts_debug:lcnt_control(mask, lists:delete(Category, PreviousMask)), + lists:member(Category, PreviousMask). %% -------------------------------------------------------------------- %% %% @@ -192,13 +229,9 @@ call(Msg) -> gen_server:call(?MODULE, Msg, infinity). %% -------------------------------------------------------------------- %% apply(M,F,As) when is_atom(M), is_atom(F), is_list(As) -> - ok = start_internal(), - Opt = lcnt:rt_opt({copy_save, true}), - lcnt:clear(), - Res = erlang:apply(M,F,As), - lcnt:collect(), - lcnt:rt_opt({copy_save, Opt}), - Res. + apply(fun() -> + erlang:apply(M,F,As) + end). apply(Fun) when is_function(Fun) -> lcnt:apply(Fun, []). @@ -209,7 +242,9 @@ apply(Fun, As) when is_function(Fun) -> lcnt:clear(), Res = erlang:apply(Fun, As), lcnt:collect(), - lcnt:rt_opt({copy_save, Opt}), + %% _ is bound to silence a dialyzer warning; it used to fail silently and + %% we don't want to change the error semantics. + _ = lcnt:rt_opt({copy_save, Opt}), Res. all_conflicts() -> all_conflicts(time). diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src index 12f0cfd2df..8beef49bf9 100644 --- a/lib/tools/src/tools.app.src +++ b/lib/tools/src/tools.app.src @@ -41,6 +41,6 @@ ] }, {runtime_dependencies, ["stdlib-3.1","runtime_tools-1.8.14", - "kernel-3.0","erts-7.0","compiler-5.0"]} + "kernel-5.4","erts-9.1","compiler-5.0"]} ] }. -- cgit v1.2.3 From 876ecc058d0d7dd48f8c5f7ddaf189d278e69925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 29 Jun 2017 19:32:41 +0200 Subject: Document rt_mask and add warnings about copy_save --- lib/tools/doc/src/lcnt.xml | 98 +++++++++++++++++++++++++++++++++----- lib/tools/doc/src/lcnt_chapter.xml | 11 +++-- 2 files changed, 94 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml index 590049e681..922c2ac0f4 100644 --- a/lib/tools/doc/src/lcnt.xml +++ b/lib/tools/doc/src/lcnt.xml @@ -5,7 +5,7 @@
2009 - 2016 + 2017 Ericsson AB, All Rights Reserved @@ -314,24 +314,22 @@ apply(Fun) -> term() Same as apply(Fun, []). + + Fun = fun() +

Same as apply(Fun, []).

apply(Fun, Args) -> term() - Clears counters, applies function and collects the profiling results. + Same as apply(Module, Function, Args). Fun = fun() Args = [term()] -

Clears the lock counters and then setups the instrumentation to save all destroyed locks. - After setup the fun is called, passing the elements in Args as arguments. - When the fun returns the statistics are immediately collected to the server. After the - collection the instrumentation is returned to its previous behavior. - The result of the applied fun is returned. -

+

Same as apply(Module, Function, Args).

@@ -349,6 +347,13 @@ collection the instrumentation is returned to its previous behavior. The result of the applied function is returned.

+ +

+ This function should only be used for micro-benchmarks; it sets copy_save + to true for the duration of the call, which can quickly lead to running + out of memory. +

+
@@ -421,6 +426,68 @@

Clear the internal counters. Same as lcnt:clear(Node).

+ + rt_mask() -> [category_atom()] + Same as rt_mask(node()). +

Same as rt_mask(node()).

+
+ + + rt_mask(Node) -> [category_atom()] + Returns the current lock category mask. + + Node = node() + + +

+ Refer to rt_mask/2 for a list of valid categories. All + categories are enabled by default. +

+
+
+ + + rt_mask(Categories) -> ok | {error, copy_save_enabled} + Same as rt_mask(node(), Categories). + + Categories = [atom()] + +

Same as rt_mask(node(), Categories).

+
+ + + rt_mask(Node, Categories) -> ok | {error, copy_save_enabled} + Changes the lock category mask. + + Node = node() + Categories = [atom()] + + +

+ Sets the lock category mask to the given categories. +

+

+ This will fail if the copy_save option is enabled; see + lcnt:rt_opt/2. +

+

Valid categories are:

+ + allocator + db (ETS tables) + debug + distribution + generic + io + process + scheduler + +

+ This list is subject to change at any time, as is the category any given lock + may belong to. +

+
+
+ rt_opt({Type, bool()}) -> bool() Same as rt_opt(node(), {Type, Opt}). @@ -434,16 +501,25 @@ Type = copy_save | process_locks -

Changes the lock counter behavior and returns the previous behaviour.

Option description:

{copy_save, bool()} - Enable statistics saving from destroyed locks by copying. This might consume a lot of memory. + Retains the statistics of destroyed locks.
Default: false + +

+ This option will use a lot of memory when enabled, which must be + reclaimed with lcnt:rt_clear. Note that it makes no distinction + between locks that were destroyed and locks for which counting was + disabled, so enabling this option will disable changes to the lock + category mask. +

+
{process_locks, bool()} - Profile process locks. + Profile process locks, equal to adding process to the lock category mask; + see lcnt:rt_mask/2
Default: true
diff --git a/lib/tools/doc/src/lcnt_chapter.xml b/lib/tools/doc/src/lcnt_chapter.xml index 1981d66117..24b58136aa 100644 --- a/lib/tools/doc/src/lcnt_chapter.xml +++ b/lib/tools/doc/src/lcnt_chapter.xml @@ -4,7 +4,7 @@
- 20092016 + 20092017 Ericsson AB. All Rights Reserved. @@ -29,7 +29,7 @@ nobody no 2009-11-26 - PA1 + PA2 lcnt_chapter.xml

@@ -97,8 +97,11 @@ ok ok

- Another way to to profile a specific function is to use lcnt:apply/3 or lcnt:apply/1 which does lcnt:clear/0 before the function and lcnt:collect/0 after its invocation. - It also sets copy_save to true for the duration of the function call + Another way to to profile a specific function is to use lcnt:apply/3 or lcnt:apply/1 + which does lcnt:clear/0 before the function and lcnt:collect/0 after its invocation. + This method should only be used in micro-benchmarks since it sets copy_save to true + for the duration of the function call, which may cause the emulator to run out of memory if + attempted under load.

 Erlang R13B03 (erts-5.7.4) [source] [smp:8:8] [rq:8] [async-threads:0] [hipe]
-- 
cgit v1.2.3