3 # Pretty-printer for simple trace backend binary trace files
5 # Copyright IBM, Corp. 2010
7 # This work is licensed under the terms of the GNU GPL, version 2. See
8 # the COPYING file in the top-level directory.
10 # For help see docs/tracing.txt
16 header_event_id
= 0xffffffffffffffff
17 header_magic
= 0xf2b177cb0aa429b4
20 trace_fmt
= '=QQQQQQQQ'
21 trace_len
= struct
.calcsize(trace_fmt
)
22 event_re
= re
.compile(r
'(disable\s+)?([a-zA-Z0-9_]+)\(([^)]*)\).*')
24 def parse_events(fobj
):
25 """Parse a trace-events file into {event_num: (name, arg1, ...)}."""
27 def get_argnames(args
):
28 """Extract argument names from a parameter list."""
29 return tuple(arg
.split()[-1].lstrip('*') for arg
in args
.split(','))
34 m
= event_re
.match(line
.strip())
38 disable
, name
, args
= m
.groups()
39 events
[event_num
] = (name
,) + get_argnames(args
)
43 def read_record(fobj
):
44 """Deserialize a trace record from a file into a tuple (event_num, timestamp, arg1, ..., arg6)."""
45 s
= fobj
.read(trace_len
)
46 if len(s
) != trace_len
:
48 return struct
.unpack(trace_fmt
, s
)
50 def read_trace_file(fobj
):
51 """Deserialize trace records from a file, yielding record tuples (event_num, timestamp, arg1, ..., arg6)."""
52 header
= read_record(fobj
)
53 if header
is None or \
54 header
[0] != header_event_id
or \
55 header
[1] != header_magic
or \
56 header
[2] != header_version
:
57 raise ValueError('not a trace file or incompatible version')
60 rec
= read_record(fobj
)
66 class Analyzer(object):
67 """A trace file analyzer which processes trace records.
69 An analyzer can be passed to run() or process(). The begin() method is
70 invoked, then each trace record is processed, and finally the end() method
73 If a method matching a trace event name exists, it is invoked to process
74 that trace record. Otherwise the catchall() method is invoked."""
77 """Called at the start of the trace."""
80 def catchall(self
, event
, rec
):
81 """Called if no specific method for processing a trace event has been found."""
85 """Called at the end of the trace."""
88 def process(events
, log
, analyzer
):
89 """Invoke an analyzer on each event in a log."""
90 if isinstance(events
, str):
91 events
= parse_events(open(events
, 'r'))
92 if isinstance(log
, str):
95 def build_fn(analyzer
, event
):
96 fn
= getattr(analyzer
, event
[0], None)
98 return analyzer
.catchall
100 event_argcount
= len(event
) - 1
101 fn_argcount
= len(inspect
.getargspec(fn
)[0]) - 1
102 if fn_argcount
== event_argcount
+ 1:
103 # Include timestamp as first argument
104 return lambda _
, rec
: fn(*rec
[1:2 + fn_argcount
])
106 # Just arguments, no timestamp
107 return lambda _
, rec
: fn(*rec
[2:2 + fn_argcount
])
111 for rec
in read_trace_file(log
):
113 event
= events
[event_num
]
114 if event_num
not in fn_cache
:
115 fn_cache
[event_num
] = build_fn(analyzer
, event
)
116 fn_cache
[event_num
](event
, rec
)
120 """Execute an analyzer on a trace file given on the command-line.
122 This function is useful as a driver for simple analysis scripts. More
123 advanced scripts will want to call process() instead."""
126 if len(sys
.argv
) != 3:
127 sys
.stderr
.write('usage: %s <trace-events> <trace-file>\n' % sys
.argv
[0])
130 events
= parse_events(open(sys
.argv
[1], 'r'))
131 process(events
, sys
.argv
[2], analyzer
)
133 if __name__
== '__main__':
134 class Formatter(Analyzer
):
136 self
.last_timestamp
= None
138 def catchall(self
, event
, rec
):
140 if self
.last_timestamp
is None:
141 self
.last_timestamp
= timestamp
142 delta_ns
= timestamp
- self
.last_timestamp
143 self
.last_timestamp
= timestamp
145 fields
= [event
[0], '%0.3f' % (delta_ns
/ 1000.0)]
146 for i
in xrange(1, len(event
)):
147 fields
.append('%s=0x%x' % (event
[i
], rec
[i
+ 1]))
148 print ' '.join(fields
)