xref: /linux/tools/perf/python/perf_live.py (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1*270e3f07SIan Rogers# SPDX-License-Identifier: GPL-2.0
2*270e3f07SIan Rogers"""
3*270e3f07SIan RogersLive event session helper using perf.evlist.
4*270e3f07SIan Rogers
5*270e3f07SIan RogersThis module provides a LiveSession class that allows running a callback
6*270e3f07SIan Rogersfor each event collected live from the system, similar to perf.session
7*270e3f07SIan Rogersbut without requiring a perf.data file.
8*270e3f07SIan Rogers"""
9*270e3f07SIan Rogers
10*270e3f07SIan Rogersimport perf
11*270e3f07SIan Rogers
12*270e3f07SIan Rogers
13*270e3f07SIan Rogersclass LiveSession:
14*270e3f07SIan Rogers    """Represents a live event collection session."""
15*270e3f07SIan Rogers
16*270e3f07SIan Rogers    def __init__(self, event_string: str, sample_callback):
17*270e3f07SIan Rogers        self.event_string = event_string
18*270e3f07SIan Rogers        self.sample_callback = sample_callback
19*270e3f07SIan Rogers        # Create a cpu map for all online CPUs
20*270e3f07SIan Rogers        self.cpus = perf.cpu_map()
21*270e3f07SIan Rogers        # Parse events and set maps
22*270e3f07SIan Rogers        self.evlist = perf.parse_events(self.event_string, self.cpus)
23*270e3f07SIan Rogers        self.evlist.config()
24*270e3f07SIan Rogers
25*270e3f07SIan Rogers    def run(self):
26*270e3f07SIan Rogers        """Run the live session."""
27*270e3f07SIan Rogers        try:
28*270e3f07SIan Rogers            self.evlist.open()
29*270e3f07SIan Rogers            self.evlist.mmap()
30*270e3f07SIan Rogers            self.evlist.enable()
31*270e3f07SIan Rogers
32*270e3f07SIan Rogers            while True:
33*270e3f07SIan Rogers                # Poll for events with 100ms timeout
34*270e3f07SIan Rogers                try:
35*270e3f07SIan Rogers                    self.evlist.poll(100)
36*270e3f07SIan Rogers                except InterruptedError:
37*270e3f07SIan Rogers                    continue
38*270e3f07SIan Rogers                for cpu in self.cpus:
39*270e3f07SIan Rogers                    for _ in range(1000): # Limit to 1000 events per CPU per poll to prevent starvation
40*270e3f07SIan Rogers                        try:
41*270e3f07SIan Rogers                            event = self.evlist.read_on_cpu(cpu)
42*270e3f07SIan Rogers                        except TypeError as e:
43*270e3f07SIan Rogers                            if "Unknown CPU" in str(e):
44*270e3f07SIan Rogers                                # CPU might be unmapped or offline, wait for mmap event
45*270e3f07SIan Rogers                                break
46*270e3f07SIan Rogers                            if "Unexpected header type" in str(e):
47*270e3f07SIan Rogers                                # Ignore valid but unsupported event types
48*270e3f07SIan Rogers                                continue
49*270e3f07SIan Rogers                            raise
50*270e3f07SIan Rogers
51*270e3f07SIan Rogers                        if event is None:
52*270e3f07SIan Rogers                            break
53*270e3f07SIan Rogers
54*270e3f07SIan Rogers                        if event.type == perf.RECORD_SAMPLE:
55*270e3f07SIan Rogers                            self.sample_callback(event)
56*270e3f07SIan Rogers        except KeyboardInterrupt:
57*270e3f07SIan Rogers            pass
58*270e3f07SIan Rogers        finally:
59*270e3f07SIan Rogers            self.evlist.close()
60