diff options
author | John Högberg <[email protected]> | 2017-07-06 12:30:45 +0200 |
---|---|---|
committer | John Högberg <[email protected]> | 2017-07-06 12:30:45 +0200 |
commit | 5a537ae41c2295f7f19e4e01fe90acc7585f5b30 (patch) | |
tree | 7a27a822fa0e7a09e2259a77683ca4f2632df0b3 /lib/tools | |
parent | 5e0019ebb29a9fd88e0b04f0bd0b0d722e3f189e (diff) | |
parent | 876ecc058d0d7dd48f8c5f7ddaf189d278e69925 (diff) | |
download | otp-5a537ae41c2295f7f19e4e01fe90acc7585f5b30.tar.gz otp-5a537ae41c2295f7f19e4e01fe90acc7585f5b30.tar.bz2 otp-5a537ae41c2295f7f19e4e01fe90acc7585f5b30.zip |
Merge branch 'john/erts/runtime-lcnt' into maint
* john/erts/runtime-lcnt:
Document rt_mask and add warnings about copy_save
Add an emulator test suite for lock counting
Break erts_debug:lock_counters/1 into separate BIFs
Allow toggling lock counting at runtime
Move lock flags to a common header
Enable register_SUITE for lcnt builds
Enable lcnt smoke test on all builds that have lcnt enabled
Make lock counter info independent of the locks being counted
OTP-14412
OTP-13170
OTP-14413
Diffstat (limited to 'lib/tools')
-rw-r--r-- | lib/tools/doc/src/lcnt.xml | 104 | ||||
-rw-r--r-- | lib/tools/doc/src/lcnt_chapter.xml | 9 | ||||
-rw-r--r-- | lib/tools/src/lcnt.erl | 77 | ||||
-rw-r--r-- | lib/tools/src/tools.app.src | 2 | ||||
-rw-r--r-- | lib/tools/test/lcnt_SUITE.erl | 5 |
5 files changed, 151 insertions, 46 deletions
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml index 31e5c241e9..922c2ac0f4 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. </p> - <note> - <p> - 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. - </p> - </note> </desc> </func> @@ -322,24 +314,22 @@ <func> <name>apply(Fun) -> term()</name> <fsummary>Same as <c>apply(Fun, [])</c>.</fsummary> + <type> + <v>Fun = fun()</v> + </type> <desc> <p>Same as <c>apply(Fun, [])</c>.</p> </desc> </func> <func> <name>apply(Fun, Args) -> term()</name> - <fsummary>Clears counters, applies function and collects the profiling results.</fsummary> + <fsummary>Same as <c>apply(Module, Function, Args)</c>.</fsummary> <type> <v>Fun = fun()</v> <v>Args = [term()]</v> </type> <desc> - <p> Clears the lock counters and then setups the instrumentation to save all destroyed locks. - After setup the fun is called, passing the elements in <c>Args</c> 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. - </p> + <p>Same as <c>apply(Module, Function, Args)</c>.</p> </desc> </func> <func> @@ -357,6 +347,13 @@ collection the instrumentation is returned to its previous behavior. The result of the applied function is returned. </p> + <warning> + <p> + This function should only be used for micro-benchmarks; it sets <c>copy_save</c> + to <c>true</c> for the duration of the call, which can quickly lead to running + out of memory. + </p> + </warning> </desc> </func> @@ -429,6 +426,68 @@ <desc> <p>Clear the internal counters. Same as <c>lcnt:clear(Node)</c>.</p></desc> </func> + <func> + <name>rt_mask() -> [category_atom()]</name> + <fsummary>Same as <c>rt_mask(node())</c>.</fsummary> + <desc><p>Same as <c>rt_mask(node())</c>.</p></desc> + </func> + + <func> + <name>rt_mask(Node) -> [category_atom()]</name> + <fsummary>Returns the current lock category mask.</fsummary> + <type> + <v>Node = node()</v> + </type> + <desc> + <p> + Refer to <c>rt_mask/2</c> for a list of valid categories. All + categories are enabled by default. + </p> + </desc> + </func> + + <func> + <name>rt_mask(Categories) -> ok | {error, copy_save_enabled}</name> + <fsummary>Same as <c>rt_mask(node(), Categories)</c>.</fsummary> + <type> + <v>Categories = [atom()]</v> + </type> + <desc><p>Same as <c>rt_mask(node(), Categories)</c>.</p></desc> + </func> + + <func> + <name>rt_mask(Node, Categories) -> ok | {error, copy_save_enabled}</name> + <fsummary>Changes the lock category mask.</fsummary> + <type> + <v>Node = node()</v> + <v>Categories = [atom()]</v> + </type> + <desc> + <p> + Sets the lock category mask to the given categories. + </p> + <p> + This will fail if the <c>copy_save</c> option is enabled; see + <c>lcnt:rt_opt/2</c>. + </p> + <p>Valid categories are:</p> + <taglist> + <item><c>allocator</c></item> + <item><c>db</c> (ETS tables)</item> + <item><c>debug</c></item> + <item><c>distribution</c></item> + <item><c>generic</c></item> + <item><c>io</c></item> + <item><c>process</c></item> + <item><c>scheduler</c></item> + </taglist> + <p> + This list is subject to change at any time, as is the category any given lock + may belong to. + </p> + </desc> + </func> + <func> <name>rt_opt({Type, bool()}) -> bool()</name> <fsummary>Same as <c>rt_opt(node(), {Type, Opt})</c>.</fsummary> @@ -442,16 +501,25 @@ <v>Type = copy_save | process_locks</v> </type> <desc> - <p>Changes the lock counter behavior and returns the previous behaviour.</p> <p>Option description:</p> <taglist> <tag><c>{copy_save, bool()}</c></tag> - <item>Enable statistics saving from destroyed locks by copying. This might consume a lot of memory. + <item>Retains the statistics of destroyed locks. <br/>Default: <c>false</c> + <warning> + <p> + This option will use a lot of memory when enabled, which must be + reclaimed with <c>lcnt:rt_clear</c>. 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. + </p> + </warning> </item> <tag><c>{process_locks, bool()}</c></tag> - <item>Profile process locks. + <item>Profile process locks, equal to adding <c>process</c> to the lock category mask; + see <c>lcnt:rt_mask/2</c> <br/>Default: <c>true</c> </item> </taglist> diff --git a/lib/tools/doc/src/lcnt_chapter.xml b/lib/tools/doc/src/lcnt_chapter.xml index c73fcb31e0..24b58136aa 100644 --- a/lib/tools/doc/src/lcnt_chapter.xml +++ b/lib/tools/doc/src/lcnt_chapter.xml @@ -29,7 +29,7 @@ <approved>nobody</approved> <checked>no</checked> <date>2009-11-26</date> - <rev>PA1</rev> + <rev>PA2</rev> <file>lcnt_chapter.xml</file> </header> <p> @@ -97,8 +97,11 @@ ok ok </pre> <p> - Another way to to profile a specific function is to use <c>lcnt:apply/3</c> or <c>lcnt:apply/1</c> which does <c>lcnt:clear/0</c> before the function and <c>lcnt:collect/0</c> after its invocation. - It also sets <c>copy_save</c> to <c>true</c> for the duration of the function call + Another way to to profile a specific function is to use <c>lcnt:apply/3</c> or <c>lcnt:apply/1</c> + which does <c>lcnt:clear/0</c> before the function and <c>lcnt:collect/0</c> after its invocation. + This method should only be used in micro-benchmarks since it sets <c>copy_save</c> to <c>true</c> + for the duration of the function call, which may cause the emulator to run out of memory if + attempted under load. </p> <pre> Erlang R13B03 (erts-5.7.4) [source] [smp:8:8] [rq:8] [async-threads:0] [hipe] 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"]} ] }. diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl index af3ce88fdd..146c915087 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"} |