diff options
Diffstat (limited to 'python/tracecmd.py')
-rw-r--r-- | python/tracecmd.py | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/python/tracecmd.py b/python/tracecmd.py new file mode 100644 index 00000000..4d481576 --- /dev/null +++ b/python/tracecmd.py @@ -0,0 +1,255 @@ +# +# Copyright (C) International Business Machines Corp., 2009 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# 2009-Dec-17: Initial version by Darren Hart <dvhltc@us.ibm.com> +# + +from functools import update_wrapper +from ctracecmd import * +from UserDict import DictMixin + +""" +Python interface to the tracecmd library for parsing ftrace traces + +Python tracecmd applications should be written to this interface. It will be +updated as the tracecmd C API changes and try to minimze the impact to python +applications. The ctracecmd Python module is automatically generated using SWIG +and it is recommended applications not use it directly. + +TODO: consider a complete class hierarchy of ftrace events... +""" + +def cached_property(func, name=None): + if name is None: + name = func.__name__ + def _get(self): + try: + return self.__cached_properties[name] + except AttributeError: + self.__cached_properties = {} + except KeyError: + pass + value = func(self) + self.__cached_properties[name] = value + return value + update_wrapper(_get, func) + def _del(self): + self.__cached_properties.pop(name, None) + return property(_get, None, _del) + +class Event(object, DictMixin): + """ + This class can be used to access event data + according to an event's record and format. + """ + def __init__(self, pevent, record, format): + self._pevent = pevent + self._record = record + self._format = format + + def __str__(self): + return "%d.%09d CPU%d %s: pid=%d comm=%s type=%d" % \ + (self.ts/1000000000, self.ts%1000000000, self.cpu, self.name, + self.num_field("common_pid"), self.comm, self.type) + + def __del__(self): + free_record(self._record) + + def __getitem__(self, n): + f = tep_find_field(self._format, n) + if f is None: + raise KeyError("no field '%s'" % n) + return Field(self._record, f) + + def keys(self): + return py_format_get_keys(self._format) + + @cached_property + def comm(self): + return tep_data_comm_from_pid(self._pevent, self.pid) + + @cached_property + def cpu(self): + return tep_record_cpu_get(self._record) + + @cached_property + def name(self): + return event_format_name_get(self._format) + + @cached_property + def pid(self): + return tep_data_pid(self._pevent, self._record) + + @cached_property + def ts(self): + return tep_record_ts_get(self._record) + + @cached_property + def type(self): + return tep_data_type(self._pevent, self._record) + + def num_field(self, name): + f = tep_find_any_field(self._format, name) + if f is None: + return None + ret, val = tep_read_number_field(f, tep_record_data_get(self._record)) + if ret: + return None + return val + + def str_field(self, name): + f = tep_find_any_field(self._format, name) + if f is None: + return None + return py_field_get_str(f, self._record) + + def stack_field(self, long_size): + return py_field_get_stack(self._pevent, self._record, self._format, + long_size) + +class TraceSeq(object): + def __init__(self, trace_seq): + self._trace_seq = trace_seq + + def puts(self, s): + return trace_seq_puts(self._trace_seq, s) + +class FieldError(Exception): + pass + +class Field(object): + def __init__(self, record, field): + self._record = record + self._field = field + + @cached_property + def data(self): + return py_field_get_data(self._field, self._record) + + def __long__(self): + ret, val = tep_read_number_field(self._field, + tep_record_data_get(self._record)) + if ret: + raise FieldError("Not a number field") + return val + __int__ = __long__ + + def __str__(self): + return py_field_get_str(self._field, self._record) + +class PEvent(object): + def __init__(self, pevent): + self._pevent = pevent + + def _handler(self, cb, s, record, event_fmt): + return cb(TraceSeq(s), Event(self._pevent, record, event_fmt)) + + def register_event_handler(self, subsys, event_name, callback): + l = lambda s, r, e: self._handler(callback, s, r, e) + + py_pevent_register_event_handler( + self._pevent, -1, subsys, event_name, l) + + @cached_property + def file_endian(self): + if tep_is_file_bigendian(self._pevent): + return '>' + return '<' + + +class FileFormatError(Exception): + pass + +class Trace(object): + """ + Trace object represents the trace file it is created with. + + The Trace object aggregates the tracecmd structures and functions that are + used to manage the trace and extract events from it. + """ + def __init__(self, filename): + self._handle = tracecmd_alloc(filename) + + if tracecmd_read_headers(self._handle): + raise FileFormatError("Invalid headers") + + if tracecmd_init_data(self._handle): + raise FileFormatError("Failed to init data") + + self._pevent = tracecmd_get_pevent(self._handle) + + @cached_property + def cpus(self): + return tracecmd_cpus(self._handle) + + @cached_property + def long_size(self): + return tracecmd_long_size(self._handle) + + def read_event(self, cpu): + rec = tracecmd_read_data(self._handle, cpu) + if rec: + type = tep_data_type(self._pevent, rec) + format = tep_find_event(self._pevent, type) + # rec ownership goes over to Event instance + return Event(self._pevent, rec, format) + return None + + def read_event_at(self, offset): + res = tracecmd_read_at(self._handle, offset) + # SWIG only returns the CPU if the record is None for some reason + if isinstance(res, int): + return None + rec, cpu = res + type = tep_data_type(self._pevent, rec) + format = tep_find_event(self._pevent, type) + # rec ownership goes over to Event instance + return Event(self._pevent, rec, format) + + def read_next_event(self): + res = tracecmd_read_next_data(self._handle) + if isinstance(res, int): + return None + rec, cpu = res + type = tep_data_type(self._pevent, rec) + format = tep_find_event(self._pevent, type) + return Event(self._pevent, rec, format) + + def peek_event(self, cpu): + rec = tracecmd_peek_data_ref(self._handle, cpu) + if rec is None: + return None + type = tep_data_type(self._pevent, rec) + format = tep_find_event(self._pevent, type) + # rec ownership goes over to Event instance + return Event(self._pevent, rec, format) + + +# Basic builtin test, execute module directly +if __name__ == "__main__": + t = Trace("trace.dat") + print("Trace contains data for %d cpus" % (t.cpus)) + + for cpu in range(0, t.cpus): + print("CPU %d" % (cpu)) + ev = t.read_event(cpu) + while ev: + print("\t%s" % (ev)) + ev = t.read_event(cpu) + + + |