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