mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
LiveSession.run() synchronously blocks while polling evlist for events. When running interactive tools or scripts from the terminal, KeyboardInterrupt exceptions raised via Ctrl-C must propagate so the caller can break out of the polling loop. Previously, LiveSession caught KeyboardInterrupt with an empty pass statement, swallowing the exception and preventing calling scripts from detecting the interrupt cleanly. Remove the empty handler so calling scripts receive KeyboardInterrupt and can perform clean teardown. Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Ian Rogers <irogers@google.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
64 lines
2.4 KiB
Python
Executable File
64 lines
2.4 KiB
Python
Executable File
# SPDX-License-Identifier: GPL-2.0
|
|
"""
|
|
Live event session helper using perf.evlist.
|
|
|
|
This module provides a LiveSession class that allows running a callback
|
|
for each event collected live from the system, similar to perf.session
|
|
but without requiring a perf.data file.
|
|
"""
|
|
|
|
import perf
|
|
|
|
|
|
class LiveSession:
|
|
"""Represents a live event collection session."""
|
|
|
|
def __init__(self, event_string: str, sample_callback):
|
|
self.event_string = event_string
|
|
self.sample_callback = sample_callback
|
|
# Create a cpu map for all online CPUs
|
|
self.cpus = perf.cpu_map()
|
|
# Parse events and set maps
|
|
self.evlist = perf.parse_events(self.event_string, self.cpus)
|
|
self.evlist.config()
|
|
|
|
def run(self):
|
|
"""Run the live session."""
|
|
try:
|
|
self.evlist.open()
|
|
self.evlist.mmap()
|
|
self.evlist.enable()
|
|
|
|
while True:
|
|
# Poll for events with 100ms timeout
|
|
try:
|
|
self.evlist.poll(100)
|
|
# CPython's PyErr_SetFromErrno() invokes PyErr_CheckSignals() on
|
|
# EINTR, converting SIGINT into KeyboardInterrupt, which inherits
|
|
# from BaseException rather than InterruptedError/OSError, so
|
|
# Ctrl-C exits the loop and triggers finally: self.evlist.close()
|
|
# while non-fatal signals retry poll().
|
|
except InterruptedError:
|
|
continue
|
|
for cpu in self.cpus:
|
|
# Limit to 1000 events per CPU per poll to prevent starvation
|
|
for _ in range(1000):
|
|
try:
|
|
event = self.evlist.read_on_cpu(cpu)
|
|
except TypeError as e:
|
|
if "Unknown CPU" in str(e):
|
|
# CPU might be unmapped or offline, wait for mmap event
|
|
break
|
|
if "Unexpected header type" in str(e):
|
|
# Ignore valid but unsupported event types
|
|
continue
|
|
raise
|
|
|
|
if event is None:
|
|
break
|
|
|
|
if event.type == perf.RECORD_SAMPLE:
|
|
self.sample_callback(event)
|
|
finally:
|
|
self.evlist.close()
|