summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorSverker Eriksson <sverker@erlang.org>2022-01-11 23:14:21 +0100
committerSverker Eriksson <sverker@erlang.org>2022-01-11 23:14:21 +0100
commit70a36dc442feda69eef375a7ffb15d4a7fb98269 (patch)
tree430e816276ba7b266c377da10a7f1a8d4c6560e0 /lib
parentf518b5d3a9bde82ebcd16697f01ed2fd1fcfd0cb (diff)
parentd33bb24ef66be325282fc75bee00dec066e6c11c (diff)
downloaderlang-70a36dc442feda69eef375a7ffb15d4a7fb98269.tar.gz
Merge branch 'maint'
Diffstat (limited to 'lib')
-rw-r--r--lib/runtime_tools/doc/src/scheduler.xml153
-rw-r--r--lib/runtime_tools/src/scheduler.erl41
-rw-r--r--lib/runtime_tools/test/scheduler_SUITE.erl25
3 files changed, 181 insertions, 38 deletions
diff --git a/lib/runtime_tools/doc/src/scheduler.xml b/lib/runtime_tools/doc/src/scheduler.xml
index 5dd721e357..e8024e00ce 100644
--- a/lib/runtime_tools/doc/src/scheduler.xml
+++ b/lib/runtime_tools/doc/src/scheduler.xml
@@ -35,12 +35,52 @@
<module since="OTP 21.0">scheduler</module>
<modulesummary>Measure scheduler utilization</modulesummary>
<description>
- <p>This module contains utility functions for easier measurement and
- calculation of scheduler utilization, otherwise obtained from calling the
- more primitive <seeerl marker="erts:erlang#statistics_scheduler_wall_time">
- <c>statistics(scheduler_wall_time)</c></seeerl>.</p>
- <p>The simplest usage is to call <seemfa marker="#utilization/1">
- <c>scheduler:utilization(Seconds)</c></seemfa>.</p>
+ <p>
+ This module contains utility functions for easy measurement and
+ calculation of scheduler utilization. It act as a wrapper around the more
+ primitive API <seeerl marker="erts:erlang#statistics_scheduler_wall_time">
+ <c>erlang:statistics(scheduler_wall_time)</c></seeerl>.
+ </p>
+ <p>
+ The simplest usage is to call the blocking <seemfa marker="#utilization/1">
+ <c>scheduler:utilization(Seconds)</c></seemfa>.
+ </p>
+ <p>
+ For non blocking and/or continuous calculation of scheduler utilization,
+ the recommended usage is:
+ </p>
+ <list>
+ <item><p>
+ First call <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>erlang:system_flag(scheduler_wall_time,true)</c></seeerl> to enable
+ scheduler wall time measurements.
+ </p></item>
+ <item><p>
+ Call <seemfa marker="#get_sample/0"><c>get_sample/0</c></seemfa> to
+ collect samples with some time in between.
+ </p></item>
+ <item><p>
+ Call <seemfa marker="#utilization/2"><c>utilization/2</c></seemfa> to
+ calculate the scheduler utilization in the interval between two
+ samples.
+ </p></item>
+ <item><p>
+ When done call
+ <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>erlang:system_flag(scheduler_wall_time,false)</c></seeerl> to disable
+ scheduler wall time measurements and avoid unecessary cpu overhead.
+ </p></item>
+ </list>
+ <p>
+ To get correct values from
+ <seemfa marker="#utilization/2"><c>utilization/2</c></seemfa>, it is
+ important that <c>scheduler_wall_time</c> is kept enabled during the
+ entire interval between the two samples. To ensure this, the process
+ that called <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>erlang:system_flag(scheduler_wall_time,true)</c></seeerl> must be
+ kept alive, as <c>scheduler_wall_time</c> will automatically be disabled
+ if it terminates.
+ </p>
</description>
<datatypes>
@@ -87,11 +127,53 @@
<funcs>
<func>
+ <name name="get_sample" arity="0" since="OTP @OTP-17830@"/>
+ <fsummary>Get scheduler utilization sample.</fsummary>
+ <desc>
+ <p>Returns a scheduler utilization sample for normal and dirty-cpu
+ schedulers. Returns <c>undefined</c> if system flag
+ <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>scheduler_wall_time</c></seeerl> has not been enabled.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="get_sample_all" arity="0" since="OTP @OTP-17830@"/>
+ <fsummary>Get scheduler utilization sample.</fsummary>
+ <desc>
+ <p>Return a scheduler utilization sample for all schedulers,
+ including dirty-io schedulers. Returns <c>undefined</c> if system flag
+ <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>scheduler_wall_time</c></seeerl> has not been enabled.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="sample" arity="0" since="OTP 21.0"/>
<fsummary>Get scheduler utilization sample.</fsummary>
<desc>
- <p>Return a scheduler utilization sample for normal and dirty-cpu
- schedulers.</p>
+ <p>
+ Return a scheduler utilization sample for normal and dirty-cpu
+ schedulers. Will call
+ <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>erlang:system_flag(scheduler_wall_time,true)</c></seeerl> first if
+ not already already enabled.
+ </p>
+ <note>
+ <p>
+ This function is <em>not recommended</em> as there is no way to detect if
+ <c>scheduler_wall_time</c> already was enabled or not. If
+ <c>scheduler_wall_time</c> has been disabled between two samples,
+ passing them to <seemfa marker="#utilization/1"><c>utilization/2</c></seemfa>
+ will yield invalid results.
+ </p>
+ <p>
+ Instead use <seemfa marker="#get_sample/0">
+ <c>get_sample/0</c></seemfa> together with
+ <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>erlang:system_flag(scheduler_wall_time,_)</c></seeerl>.
+ </p>
+ </note>
</desc>
</func>
@@ -99,8 +181,22 @@
<name name="sample_all" arity="0" since="OTP 21.0"/>
<fsummary>Get scheduler utilization sample.</fsummary>
<desc>
- <p>Return a scheduler utilization sample for all schedulers,
- including dirty-io schedulers.</p>
+ <p>
+ Return a scheduler utilization sample for all schedulers,
+ including dirty-io schedulers. Will call
+ <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>erlang:system_flag(scheduler_wall_time,true)</c></seeerl> first if
+ not already already enabled.
+ </p>
+ <note>
+ <p>
+ This function is <em>not recommended</em> for same reason as <c>sample/0</c>.
+ Instead use <seemfa marker="#get_sample_all/0">
+ <c>get_sample_all/0</c></seemfa> together with
+ <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>erlang:system_flag(scheduler_wall_time,_)</c></seeerl>.
+ </p>
+ </note>
</desc>
</func>
@@ -108,8 +204,15 @@
<name name="utilization" arity="1" clause_i="1" since="OTP 21.0"/>
<fsummary>Measure scheduler utilizations during a period of time.</fsummary>
<desc>
- <p>Measure utilization for normal and dirty-cpu schedulers during
- <c><anno>Seconds</anno></c> seconds, and then return the result.</p>
+ <p>
+ Measure utilization for normal and dirty-cpu schedulers during
+ <c><anno>Seconds</anno></c> seconds, and then return the result.
+ </p>
+ <p>
+ Will automatically first enable and then disable
+ <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>scheduler_wall_time</c></seeerl>.
+ </p>
</desc>
</func>
@@ -122,11 +225,8 @@
<c>scheduler:utilization(Sample, scheduler:sample_all())</c>.</p>
<note>
<p>
- Scheduler utilization is measured as an average value over a time
- interval, calculated as the difference between two samples. To get
- good useful utilization values at least a couple of seconds should
- have passed between the two samples. For this reason, you should not
- do
+ This function is <em>not recommended</em> as it's so easy to get invalid
+ results without noticing. In particular do not do this:
</p>
<pre>
scheduler:utilization(scheduler:sample()). % DO NOT DO THIS!
@@ -137,10 +237,10 @@ scheduler:utilization(scheduler:sample()). % DO NOT DO THIS!
probably be more misleading than informative.
</p>
<p>
- Instead use <seemfa marker="#utilization/1">
- <c>scheduler:utilization(Seconds)</c></seemfa> or let some time pass
- between <c>Sample=scheduler:sample()</c> and
- <c>scheduler:utilization(Sample)</c>.
+ Instead use <seemfa marker="#utilization/2">
+ <c>scheduler:utilization/2</c></seemfa> and call
+ <seemfa marker="#get_sample/0"><c>get_sample/0</c></seemfa> to get
+ samples with some time in between.
</p>
</note>
</desc>
@@ -152,8 +252,15 @@ scheduler:utilization(scheduler:sample()). % DO NOT DO THIS!
<desc>
<p>Calculates scheduler utilizations for the time interval between
the two samples obtained from calling
- <seemfa marker="#sample/0"><c>sample/0</c></seemfa> or
- <seemfa marker="#sample_all/0"><c>sample_all/0</c></seemfa>.</p>
+ <seemfa marker="#sample/0"><c>get_sample/0</c></seemfa> or
+ <seemfa marker="#sample_all/0"><c>get_sample_all/0</c></seemfa>.</p>
+ <p>
+ This function itself, does not need
+ <seeerl marker="erts:erlang#system_flag_scheduler_wall_time">
+ <c>scheduler_wall_time</c></seeerl> to be enabled. However, for
+ a correct result, <c>scheduler_wall_time</c> must have been enabled
+ during the entire interval between the two samples.
+ </p>
</desc>
</func>
diff --git a/lib/runtime_tools/src/scheduler.erl b/lib/runtime_tools/src/scheduler.erl
index c896b671ac..114dfa17fe 100644
--- a/lib/runtime_tools/src/scheduler.erl
+++ b/lib/runtime_tools/src/scheduler.erl
@@ -23,8 +23,8 @@
-module(scheduler).
--export([sample/0,
- sample_all/0,
+-export([sample/0, get_sample/0,
+ sample_all/0, get_sample_all/0,
utilization/1,
utilization/2]).
@@ -54,12 +54,32 @@ sample(Stats) ->
sample(Stats);
List ->
- Sorted = lists:sort(List),
- Tagged = lists:map(fun({I, A, T}) -> {sched_tag(I), I, A, T} end,
- Sorted),
- {Stats, Tagged}
+ create_sample(Stats, List)
end.
+-spec get_sample() -> sched_sample() | undefined.
+get_sample() ->
+ get_sample(scheduler_wall_time).
+
+-spec get_sample_all() -> sched_sample() | undefined.
+get_sample_all() ->
+ get_sample(scheduler_wall_time_all).
+
+get_sample(Stats) ->
+ case erlang:statistics(Stats) of
+ undefined ->
+ undefined;
+ List ->
+ create_sample(Stats, List)
+ end.
+
+create_sample(Stats, List) ->
+ Sorted = lists:sort(List),
+ Tagged = lists:map(fun({I, A, T}) -> {sched_tag(I), I, A, T} end,
+ Sorted),
+ {Stats, Tagged}.
+
+
-type sched_util_result() ::
[{sched_type(), sched_id(), float(), string()} |
{total, float(), string()} |
@@ -70,16 +90,11 @@ sample(Stats) ->
(Sample) -> sched_util_result() when
Sample :: sched_sample().
utilization(Seconds) when is_integer(Seconds), Seconds > 0 ->
- OldFlag = erlang:system_flag(scheduler_wall_time, true),
+ _ = erlang:system_flag(scheduler_wall_time, true),
T0 = sample(),
receive after Seconds*1000 -> ok end,
T1 = sample(),
- case OldFlag of
- false ->
- erlang:system_flag(scheduler_wall_time, OldFlag);
- true ->
- ok
- end,
+ _ = erlang:system_flag(scheduler_wall_time, false),
utilization(T0,T1);
utilization({Stats, _}=T0) when Stats =:= scheduler_wall_time;
diff --git a/lib/runtime_tools/test/scheduler_SUITE.erl b/lib/runtime_tools/test/scheduler_SUITE.erl
index 1c80253371..fd1ec4cc8e 100644
--- a/lib/runtime_tools/test/scheduler_SUITE.erl
+++ b/lib/runtime_tools/test/scheduler_SUITE.erl
@@ -23,15 +23,26 @@
-export([suite/0, all/0]).
%% Test cases
--export([basic/1]).
+-export([basic/1,
+ utilization_disable/1]).
-all() -> [basic].
+all() -> [basic,
+ utilization_disable].
suite() -> [{ct_hooks,[ts_install_cth]}].
basic(_Config) ->
+ undefined = scheduler:get_sample(),
+ undefined = scheduler:get_sample_all(),
+ false = erlang:system_flag(scheduler_wall_time, true),
+ GS1 = scheduler:get_sample(),
+ GS2 = scheduler:get_sample_all(),
+ check(scheduler:utilization(GS1, scheduler:get_sample())),
+ check(scheduler:utilization(GS2, scheduler:get_sample())),
+ true = erlang:system_flag(scheduler_wall_time, false),
+
S1 = scheduler:sample(),
S2 = scheduler:sample_all(),
@@ -56,6 +67,16 @@ basic(_Config) ->
ok.
+%% OTP-17800: Test that utilization(Seconds) restores scheduler_wall_time flag
+%% even when it already was globally enabled.
+utilization_disable(_Config) ->
+ false = erlang:system_flag(scheduler_wall_time, true),
+ check(scheduler:utilization(1)),
+ true = erlang:system_flag(scheduler_wall_time, false),
+
+ undefined = scheduler:get_sample(),
+ ok.
+
check([{total, Tf, Ts} | List]=U) ->
io:format("\nU = ~p\n", [U]),