From 928ca754b54af8ff9bca7ca9419978e826e2ef4e Mon Sep 17 00:00:00 2001 From: "Gabriele N. Tornetta" Date: Fri, 9 Oct 2026 12:39:55 +0100 Subject: [PATCH] fix: exclude idle samples from CPU stats In full mode, AustinStats counted the time of idle samples towards CPU statistics, making them identical to wall time. Idle samples now only contribute to wall time, consistent with the speedscope and pprof exporters. --- austin/stats.py | 5 ++++- test/stats/test_austin_stats.py | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/austin/stats.py b/austin/stats.py index f1d64cc..72614db 100644 --- a/austin/stats.py +++ b/austin/stats.py @@ -270,8 +270,11 @@ def update(self, sample: AustinSample) -> None: by using :func:`Sample.parse` on a sample string passed by Austin to the sample callback. """ - if self.stats_type in {AustinStatsType.WALL, AustinStatsType.CPU}: + if self.stats_type is AustinStatsType.WALL: metric = sample.metrics.time + elif self.stats_type is AustinStatsType.CPU: + # In full mode, idle samples contribute wall time but no CPU time + metric = None if sample.idle else sample.metrics.time elif ( self.stats_type is AustinStatsType.MEMORY_ALLOC and sample.metrics.memory is not None diff --git a/test/stats/test_austin_stats.py b/test/stats/test_austin_stats.py index 3f18deb..19313d7 100644 --- a/test/stats/test_austin_stats.py +++ b/test/stats/test_austin_stats.py @@ -25,6 +25,7 @@ from copy import deepcopy from austin.events import AustinFrame +from austin.events import AustinMetadata from austin.events import AustinMetrics from austin.events import AustinSample from austin.format.collapsed_stack import AustinFileReader @@ -348,3 +349,39 @@ def test_deepcopy(): cloned_stats = deepcopy(stats) assert cloned_stats == stats assert cloned_stats is not stats + + +def test_load_full_mode_cpu_excludes_idle(): + frames = (AustinFrame("foo_module.py", "foo", 10),) + stats = AustinStats.load( + iter( + [ + AustinMetadata("mode", "full"), + AustinSample( + pid=42, + iid=0, + thread="0x7f45645646", + metrics=AustinMetrics(time=100, memory=10), + frames=frames, + idle=False, + ), + AustinSample( + pid=42, + iid=0, + thread="0x7f45645646", + metrics=AustinMetrics(time=900, memory=-5), + frames=frames, + idle=True, + ), + ] + ) + ) + + def total(stats_type: AustinStatsType) -> int: + thread_info = ThreadInfo("0x7f45645646", 0) + return stats[stats_type].processes[42].threads[thread_info].total + + assert total(AustinStatsType.CPU) == 100 + assert total(AustinStatsType.WALL) == 1000 + assert total(AustinStatsType.MEMORY_ALLOC) == 10 + assert total(AustinStatsType.MEMORY_DEALLOC) == 5