[DTrace-devel] [PATCH v2 4/4] EINTR: respect switchrate in the core polling loop
Nick Alcock
nick.alcock at oracle.com
Wed Nov 1 15:31:06 UTC 2023
This fix causes the core polling loop to check for -EINTR and retry until
the timeout expires, and adds a test to verify that hammering dtrace with
SIGWINCH (a usually-ignored signal) does not cause the timeout to end early,
as it does now.
I'm not sure this is actually desirable behaviour, since it stops
dtrace_work() returning -EINTR, which clients might in theory be relying on
(yeah right, I don't believe it either, but maybe they are).
Obviously if we don't do this the test is also not desirable. I honestly
don't think we should do it... but I'll post this anyway in case anyone
disagrees.
Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
---
libdtrace/dt_consume.c | 33 +++++++++++--
test/unittest/rates/tst.switchrate-eintr.sh | 55 +++++++++++++++++++++
2 files changed, 83 insertions(+), 5 deletions(-)
create mode 100755 test/unittest/rates/tst.switchrate-eintr.sh
diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c
index f5dfc373caea7..e4c319bde89de 100644
--- a/libdtrace/dt_consume.c
+++ b/libdtrace/dt_consume.c
@@ -3027,9 +3027,12 @@ dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf,
dtrace_consume_rec_f *rf, void *arg)
{
dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_SWITCHRATE];
+ int64_t timeout_msec;
struct epoll_event events[dtp->dt_conf.num_online_cpus];
int drained = 0;
int i, cnt;
+ struct timespec start, end;
+ int no_adjustment = 0;
dtrace_workstatus_t rval;
/* Has tracing started yet? */
@@ -3062,12 +3065,32 @@ dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf,
/*
* The epoll_wait() function expects the interval to be expressed in
* milliseconds whereas the switch rate is expressed in nanoseconds.
- * We therefore need to convert the value.
+ * We therefore need to convert the value, and shift it into a signed
+ * regime so that timeout adjustment can go negative (if an EINTR hits
+ * when the timer has already overrun, which in the presence of timer
+ * slack is not only possible but likely).
*/
- interval /= NANOSEC / MILLISEC;
- cnt = epoll_wait(dtp->dt_poll_fd, events, dtp->dt_conf.num_online_cpus,
- interval);
- if (cnt < 0) {
+ timeout_msec = interval / (NANOSEC / MILLISEC);
+
+ if (clock_gettime(CLOCK_REALTIME, &start) < 0)
+ no_adjustment = 1;
+
+ while ((cnt = epoll_wait(dtp->dt_poll_fd, events,
+ dtp->dt_conf.num_online_cpus, timeout_msec)) < 0 &&
+ errno == EINTR) {
+
+ if (no_adjustment || clock_gettime(CLOCK_REALTIME, &end) < 0)
+ continue; /* Abandon timeout adjustment */
+
+ timeout_msec -= ((end.tv_sec + ((unsigned long long) end.tv_nsec * NANOSEC)) -
+ (start.tv_sec + ((unsigned long long) start.tv_nsec * NANOSEC))) /
+ MICROSEC;
+
+ if (timeout_msec < 0)
+ timeout_msec = 0;
+ }
+
+ if (cnt < 0) {
dt_set_errno(dtp, errno);
return DTRACE_WORKSTATUS_ERROR;
}
diff --git a/test/unittest/rates/tst.switchrate-eintr.sh b/test/unittest/rates/tst.switchrate-eintr.sh
new file mode 100755
index 0000000000000..efd2c1a618659
--- /dev/null
+++ b/test/unittest/rates/tst.switchrate-eintr.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+# Licensed under the Universal Permissive License v 1.0 as shown at
+# http://oss.oracle.com/licenses/upl.
+#
+
+# @@nosort
+
+dtrace=$1
+
+# Currently, there are few easily visible effects of changing the switchrate.
+# One way to check is to run a very simple D script that should run
+# "instantaneously" -- except that its termination will be delayed by a
+# long switchrate. Meanwhile, hammer dtrace with SIGWINCH signals once a
+# second, and see if we still get the switchrate more or less right. (If we
+# treat EINTR as an interrupting signal we will end much too early; if we
+# handle it but fail to adjust the timeout, we will never terminate.)
+
+timed_trace() {
+ return $(/usr/bin/time -f "%e" \
+ $dtrace -xswitchrate=${1}sec -qn 'BEGIN { exit(0) }' \
+ |& awk 'NF != 0 {print int($1 + 0.5)}')
+}
+
+sighit() {
+ while pkill --signal WINCH --session $1 dtrace > /dev/null 2>&1; do
+ sleep 1
+ done
+}
+
+sid=$(ps -p $$ -o sid=)
+
+status=0
+for nexpect in 1 8; do
+ # Run the "instantaneous" D script with the prescribed switchrate.
+ # Time it. Round to the nearest number of seconds with int(t+0.5).
+ timed_trace $nexpect &
+ timed=$!
+
+ sighit $sid
+ wait -f $timed
+ nactual=$?
+ echo nactual is "$nactual"
+ wait # let sighit die
+
+ # Check the actual number of seconds to the expected value.
+ # Actually, the actual time might be a few seconds longer than expected.
+ # So pad $nexpect.
+ test/utils/check_result.sh $nactual $(($nexpect + 3)) 4
+ status=$(($status + $?))
+done
+
+exit $status
--
2.42.0.271.g85384428f1
More information about the DTrace-devel
mailing list