From ebf54f8ac4d2abc02ce6ab4c9189dbb6afc15ed9 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Thu, 12 Mar 2026 16:22:44 +0100 Subject: [PATCH] tools/ustat: Properly handle SIGINT in ustat Most interactive tools waiting for a keyboard interrupt just expect it during sleep() call. In most case it works well enough since most of the time is typically spend in sleep. In the case of tclstat however, the tool can spend a lot of time detaching uprobes, in self.bpf.cleanup() more specifically and it fails when CTRL+C is pressed. Fix this by catching the SIGINT signal. We still want to raise KeyboardInterrupt while sleeping though, for the tool to exit immediately when possible. Fixes the following error: 10:12:42 loadavg: 0.03 0.05 0.01 2/289 21928 PID CMDLINE METHOD/s GC/s OBJNEW/s CLOAD/s EXC/s THR/s 21926 tclsh ./fib.tcl 2 2 0 8 0 0 0 ^Cioctl(PERF_EVENT_IOC_DISABLE) failed: Bad file descriptor close perf event FD failed: Bad file descriptor Exception ignored in atexit callback: > Traceback (most recent call last): File "/usr/lib/python3.12/site-packages/bcc/__init__.py", line 1855, in cleanup self.detach_uprobe_event(k) File "/usr/lib/python3.12/site-packages/bcc/__init__.py", line 1507, in detach_uprobe_event raise Exception("Failed to detach BPF from uprobe") Exception: Failed to detach BPF from uprobe Signed-off-by: Jerome Marchand --- tools/lib/ustat.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/lib/ustat.py b/tools/lib/ustat.py index 3f4287bfe8b0..85f38fd0110f 100755 --- a/tools/lib/ustat.py +++ b/tools/lib/ustat.py @@ -25,6 +25,7 @@ import sys from subprocess import call from time import sleep, strftime +import signal class Category(object): THREAD = "THREAD" @@ -242,10 +243,17 @@ def _detach_probes(self): self.bpf.cleanup() # Cleans up all attached probes self.bpf = None + def _sigint_handler(self, signum, frame): + self.exiting = True; + if self.in_sleep: + raise KeyboardInterrupt + def _loop_iter(self): self._attach_probes() try: + self.in_sleep = True sleep(self.args.interval) + self.in_sleep = False except KeyboardInterrupt: self.exiting = True @@ -293,6 +301,7 @@ def run(self): self.args.interval) countdown = self.args.count self.exiting = False + signal.signal(signal.SIGINT, self._sigint_handler) while True: self._loop_iter() countdown -= 1