[PATCH 3/6] cyclictest: Add overflow bucket to handle latencies exceeding histogram range
John Kacur <[email protected]> Thu, 23 Jul 2026 15:47:48 -0400
| Newsgroups | org.kernel.vger.linux-rt-users |
|---|---|
| Message-ID | <[email protected]> |
Previously, when maximum latency exceeded the configured histogram range
(buckets parameter), rteval would skip generating statistics entirely and
return an error. This was problematic, for example when running on non-RT
systems where occasional latency spikes are expected.
This commit implements an overflow bucket mechanism:
- Samples exceeding the histogram range are clamped to the last bucket
- The actual maximum latency is still tracked and reported
- Overflow sample count is tracked and reported in XML output
- Statistics are now generated even when max > buckets
- Error messages changed to warnings with overflow information
Benefits:
- Users get statistics even with latency spikes on non-RT systems
- No need to guess the right bucket size in advance
- Clear indication when overflow occurs via warnings and XML metadata
- XML output includes overflow_samples count and overflow_bucket index
Example XML output when overflow occurs:
<statistics>
...
<overflow_samples info="42 samples exceeded histogram range">42</overflow_samples>
</statistics>
<histogram nbuckets="3500" overflow_bucket="3499" overflow_count="42">
...
</histogram>
This addresses the issue where rteval would fail to produce useful data,
for example when running on non-RT kernels with occasional high latency spikes.
---
rteval/modules/measurement/cyclictest.py | 38 +++++++++++++++++++++---
1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
index eee315d270f8..8371aa2e2aa5 100644
--- a/rteval/modules/measurement/cyclictest.py
+++ b/rteval/modules/measurement/cyclictest.py
@@ -38,6 +38,8 @@ class RunData:
self.__range = 0.0
self.__mad = 0.0
self._log = logfnc
+ self.__overflow_samples = 0 # Track samples that exceeded bucket range
+ self.__bucket_limit = None # Maximum bucket index
def __str__(self):
retval = f"id: {self.__id}\n"
@@ -61,11 +63,28 @@ class RunData:
if value < self.__min:
self.__min = value
+ def set_bucket_limit(self, limit):
+ """Set the maximum bucket index for overflow handling"""
+ self.__bucket_limit = limit
+
+ def get_overflow_count(self):
+ """Get the number of samples that exceeded the bucket limit"""
+ return self.__overflow_samples
+
def bucket(self, index, value):
- self.__samples[index] = self.__samples.setdefault(index, 0) + value
+ """Add samples to histogram bucket, clamping overflow to the last bucket"""
+ # Track the actual max before clamping
if value:
self.update_max(index)
self.update_min(index)
+
+ # Clamp to bucket limit if set (overflow bucket)
+ if self.__bucket_limit is not None and index >= self.__bucket_limit:
+ if value:
+ self.__overflow_samples += value
+ index = self.__bucket_limit - 1 # Use last bucket as overflow bucket
+
+ self.__samples[index] = self.__samples.setdefault(index, 0) + value
self.__numsamples += value
def reduce(self):
@@ -166,8 +185,16 @@ class RunData:
n = stat_n.newTextChild(None, 'standard_deviation', str(self.__stddev))
n.newProp('unit', 'us')
+ # Report overflow samples if any
+ if self.__overflow_samples > 0:
+ n = stat_n.newTextChild(None, 'overflow_samples', str(self.__overflow_samples))
+ n.newProp('info', f'{self.__overflow_samples} samples exceeded histogram range')
+
hist_n = rep_n.newChild(None, 'histogram', None)
hist_n.newProp('nbuckets', str(len(self.__samples)))
+ if self.__overflow_samples > 0:
+ hist_n.newProp('overflow_bucket', str(self.__bucket_limit - 1))
+ hist_n.newProp('overflow_count', str(self.__overflow_samples))
keys = list(self.__samples.keys())
keys.sort()
for k in keys:
@@ -205,12 +232,14 @@ class Cyclictest(rtevalModulePrototype):
self.__cyclicdata[core] = RunData(core, 'core', self.__priority,
logfnc=self._log)
self.__cyclicdata[core].description = info[core]['model name']
+ self.__cyclicdata[core].set_bucket_limit(self.__buckets)
# Create a RunData object for the overall system
self.__cyclicdata['system'] = RunData('system',
'system', self.__priority,
logfnc=self._log)
self.__cyclicdata['system'].description = (f"({self.__numcores} cores) ") + info['0']['model name']
+ self.__cyclicdata['system'].set_bucket_limit(self.__buckets)
# Logging configuration
self._logging = self.__cfg.setdefault('logging', False)
@@ -480,9 +509,10 @@ class Cyclictest(rtevalModulePrototype):
# Let the user know if max latency overshot the number of buckets
if self.__cyclicdata["system"].get_max() > self.__buckets:
- self._log(Log.ERR, f'Max latency({self.__cyclicdata["system"].get_max()}us) exceeded histogram range({self.__buckets}us). Skipping statistics')
- self._log(Log.ERR, "Increase number of buckets to avoid lost samples")
- return rep_n
+ overflow_count = self.__cyclicdata["system"].get_overflow_count()
+ self._log(Log.WARN, f'Max latency({self.__cyclicdata["system"].get_max()}us) exceeded histogram range({self.__buckets}us)')
+ self._log(Log.WARN, f'{overflow_count} samples clamped to overflow bucket at index {self.__buckets - 1}')
+ self._log(Log.WARN, "Consider increasing buckets parameter for better resolution")
rep_n.addChild(self.__cyclicdata["system"].MakeReport())
for thr in self.__cpus:
--
2.55.0