xref: /aosp_15_r20/external/trace-cmd/python/tracecmd.py (revision 58e6ee5f017f6a8912852c892d18457e4bafb554)
1#
2# Copyright (C) International Business Machines  Corp., 2009
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software
16# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17#
18# 2009-Dec-17:	Initial version by Darren Hart <[email protected]>
19#
20
21from functools import update_wrapper
22from ctracecmd import *
23from UserDict import DictMixin
24
25"""
26Python interface to the tracecmd library for parsing ftrace traces
27
28Python tracecmd applications should be written to this interface. It will be
29updated as the tracecmd C API changes and try to minimze the impact to python
30applications. The ctracecmd Python module is automatically generated using SWIG
31and it is recommended applications not use it directly.
32
33TODO: consider a complete class hierarchy of ftrace events...
34"""
35
36def cached_property(func, name=None):
37    if name is None:
38        name = func.__name__
39    def _get(self):
40        try:
41            return self.__cached_properties[name]
42        except AttributeError:
43            self.__cached_properties = {}
44        except KeyError:
45            pass
46        value = func(self)
47        self.__cached_properties[name] = value
48        return value
49    update_wrapper(_get, func)
50    def _del(self):
51        self.__cached_properties.pop(name, None)
52    return property(_get, None, _del)
53
54class Event(object, DictMixin):
55    """
56    This class can be used to access event data
57    according to an event's record and format.
58    """
59    def __init__(self, pevent, record, format):
60        self._pevent = pevent
61        self._record = record
62        self._format = format
63
64    def __str__(self):
65        return "%d.%09d CPU%d %s: pid=%d comm=%s type=%d" % \
66               (self.ts/1000000000, self.ts%1000000000, self.cpu, self.name,
67                self.num_field("common_pid"), self.comm, self.type)
68
69    def __del__(self):
70        free_record(self._record)
71
72    def __getitem__(self, n):
73        f = tep_find_field(self._format, n)
74        if f is None:
75            raise KeyError("no field '%s'" % n)
76        return Field(self._record, f)
77
78    def keys(self):
79        return py_format_get_keys(self._format)
80
81    @cached_property
82    def comm(self):
83        return tep_data_comm_from_pid(self._pevent, self.pid)
84
85    @cached_property
86    def cpu(self):
87        return tep_record_cpu_get(self._record)
88
89    @cached_property
90    def name(self):
91        return event_format_name_get(self._format)
92
93    @cached_property
94    def pid(self):
95        return tep_data_pid(self._pevent, self._record)
96
97    @cached_property
98    def ts(self):
99        return tep_record_ts_get(self._record)
100
101    @cached_property
102    def type(self):
103        return tep_data_type(self._pevent, self._record)
104
105    def num_field(self, name):
106        f = tep_find_any_field(self._format, name)
107        if f is None:
108            return None
109        ret, val = tep_read_number_field(f, tep_record_data_get(self._record))
110        if ret:
111            return None
112        return val
113
114    def str_field(self, name):
115        f = tep_find_any_field(self._format, name)
116        if f is None:
117            return None
118        return py_field_get_str(f, self._record)
119
120    def stack_field(self, long_size):
121        return py_field_get_stack(self._pevent, self._record, self._format,
122                                  long_size)
123
124class TraceSeq(object):
125    def __init__(self, trace_seq):
126        self._trace_seq = trace_seq
127
128    def puts(self, s):
129        return trace_seq_puts(self._trace_seq, s)
130
131class FieldError(Exception):
132    pass
133
134class Field(object):
135    def __init__(self, record, field):
136        self._record = record
137        self._field = field
138
139    @cached_property
140    def data(self):
141        return py_field_get_data(self._field, self._record)
142
143    def __long__(self):
144        ret, val =  tep_read_number_field(self._field,
145                                          tep_record_data_get(self._record))
146        if ret:
147            raise FieldError("Not a number field")
148        return val
149    __int__ = __long__
150
151    def __str__(self):
152        return py_field_get_str(self._field, self._record)
153
154class PEvent(object):
155    def __init__(self, pevent):
156        self._pevent = pevent
157
158    def _handler(self, cb, s, record, event_fmt):
159        return cb(TraceSeq(s), Event(self._pevent, record, event_fmt))
160
161    def register_event_handler(self, subsys, event_name, callback):
162        l = lambda s, r, e: self._handler(callback, s, r, e)
163
164        py_pevent_register_event_handler(
165                  self._pevent, -1, subsys, event_name, l)
166
167    @cached_property
168    def file_endian(self):
169        if tep_is_file_bigendian(self._pevent):
170            return '>'
171        return '<'
172
173
174class FileFormatError(Exception):
175    pass
176
177class Trace(object):
178    """
179    Trace object represents the trace file it is created with.
180
181    The Trace object aggregates the tracecmd structures and functions that are
182    used to manage the trace and extract events from it.
183    """
184    def __init__(self, filename):
185        self._handle = tracecmd_alloc(filename)
186
187        if tracecmd_read_headers(self._handle):
188            raise FileFormatError("Invalid headers")
189
190        if tracecmd_init_data(self._handle):
191            raise FileFormatError("Failed to init data")
192
193        self._pevent = tracecmd_get_pevent(self._handle)
194
195    @cached_property
196    def cpus(self):
197        return tracecmd_cpus(self._handle)
198
199    @cached_property
200    def long_size(self):
201        return tracecmd_long_size(self._handle)
202
203    def read_event(self, cpu):
204        rec = tracecmd_read_data(self._handle, cpu)
205        if rec:
206            type = tep_data_type(self._pevent, rec)
207            format = tep_find_event(self._pevent, type)
208            # rec ownership goes over to Event instance
209            return Event(self._pevent, rec, format)
210        return None
211
212    def read_event_at(self, offset):
213        res = tracecmd_read_at(self._handle, offset)
214        # SWIG only returns the CPU if the record is None for some reason
215        if isinstance(res, int):
216            return None
217        rec, cpu = res
218        type = tep_data_type(self._pevent, rec)
219        format = tep_find_event(self._pevent, type)
220        # rec ownership goes over to Event instance
221        return Event(self._pevent, rec, format)
222
223    def read_next_event(self):
224        res = tracecmd_read_next_data(self._handle)
225        if isinstance(res, int):
226            return None
227        rec, cpu = res
228        type = tep_data_type(self._pevent, rec)
229        format = tep_find_event(self._pevent, type)
230        return Event(self._pevent, rec, format)
231
232    def peek_event(self, cpu):
233        rec = tracecmd_peek_data_ref(self._handle, cpu)
234        if rec is None:
235            return None
236        type = tep_data_type(self._pevent, rec)
237        format = tep_find_event(self._pevent, type)
238        # rec ownership goes over to Event instance
239        return Event(self._pevent, rec, format)
240
241
242# Basic builtin test, execute module directly
243if __name__ == "__main__":
244    t = Trace("trace.dat")
245    print("Trace contains data for %d cpus" % (t.cpus))
246
247    for cpu in range(0, t.cpus):
248        print("CPU %d" % (cpu))
249        ev = t.read_event(cpu)
250        while ev:
251            print("\t%s" % (ev))
252            ev = t.read_event(cpu)
253
254
255
256