Statistics
| Branch: | Revision:

root / scripts / simpletrace.py @ 49926043

History | View | Annotate | Download (4.6 kB)

1 26f7227b Stefan Hajnoczi
#!/usr/bin/env python
2 26f7227b Stefan Hajnoczi
#
3 26f7227b Stefan Hajnoczi
# Pretty-printer for simple trace backend binary trace files
4 26f7227b Stefan Hajnoczi
#
5 26f7227b Stefan Hajnoczi
# Copyright IBM, Corp. 2010
6 26f7227b Stefan Hajnoczi
#
7 26f7227b Stefan Hajnoczi
# This work is licensed under the terms of the GNU GPL, version 2.  See
8 26f7227b Stefan Hajnoczi
# the COPYING file in the top-level directory.
9 26f7227b Stefan Hajnoczi
#
10 26f7227b Stefan Hajnoczi
# For help see docs/tracing.txt
11 26f7227b Stefan Hajnoczi
12 26f7227b Stefan Hajnoczi
import struct
13 26f7227b Stefan Hajnoczi
import re
14 59da6684 Stefan Hajnoczi
import inspect
15 26f7227b Stefan Hajnoczi
16 26f7227b Stefan Hajnoczi
header_event_id = 0xffffffffffffffff
17 26f7227b Stefan Hajnoczi
header_magic    = 0xf2b177cb0aa429b4
18 26f7227b Stefan Hajnoczi
header_version  = 0
19 0b5538c3 Stefan Hajnoczi
dropped_event_id = 0xfffffffffffffffe
20 26f7227b Stefan Hajnoczi
21 26f7227b Stefan Hajnoczi
trace_fmt = '=QQQQQQQQ'
22 26f7227b Stefan Hajnoczi
trace_len = struct.calcsize(trace_fmt)
23 6df40080 Stefan Hajnoczi
event_re  = re.compile(r'(disable\s+)?([a-zA-Z0-9_]+)\(([^)]*)\).*')
24 26f7227b Stefan Hajnoczi
25 26f7227b Stefan Hajnoczi
def parse_events(fobj):
26 59da6684 Stefan Hajnoczi
    """Parse a trace-events file into {event_num: (name, arg1, ...)}."""
27 26f7227b Stefan Hajnoczi
28 26f7227b Stefan Hajnoczi
    def get_argnames(args):
29 26f7227b Stefan Hajnoczi
        """Extract argument names from a parameter list."""
30 26f7227b Stefan Hajnoczi
        return tuple(arg.split()[-1].lstrip('*') for arg in args.split(','))
31 26f7227b Stefan Hajnoczi
32 0b5538c3 Stefan Hajnoczi
    events = {dropped_event_id: ('dropped', 'count')}
33 26f7227b Stefan Hajnoczi
    event_num = 0
34 26f7227b Stefan Hajnoczi
    for line in fobj:
35 26f7227b Stefan Hajnoczi
        m = event_re.match(line.strip())
36 26f7227b Stefan Hajnoczi
        if m is None:
37 26f7227b Stefan Hajnoczi
            continue
38 26f7227b Stefan Hajnoczi
39 6df40080 Stefan Hajnoczi
        disable, name, args = m.groups()
40 26f7227b Stefan Hajnoczi
        events[event_num] = (name,) + get_argnames(args)
41 26f7227b Stefan Hajnoczi
        event_num += 1
42 26f7227b Stefan Hajnoczi
    return events
43 26f7227b Stefan Hajnoczi
44 26f7227b Stefan Hajnoczi
def read_record(fobj):
45 59da6684 Stefan Hajnoczi
    """Deserialize a trace record from a file into a tuple (event_num, timestamp, arg1, ..., arg6)."""
46 26f7227b Stefan Hajnoczi
    s = fobj.read(trace_len)
47 26f7227b Stefan Hajnoczi
    if len(s) != trace_len:
48 26f7227b Stefan Hajnoczi
        return None
49 26f7227b Stefan Hajnoczi
    return struct.unpack(trace_fmt, s)
50 26f7227b Stefan Hajnoczi
51 26f7227b Stefan Hajnoczi
def read_trace_file(fobj):
52 59da6684 Stefan Hajnoczi
    """Deserialize trace records from a file, yielding record tuples (event_num, timestamp, arg1, ..., arg6)."""
53 26f7227b Stefan Hajnoczi
    header = read_record(fobj)
54 26f7227b Stefan Hajnoczi
    if header is None or \
55 26f7227b Stefan Hajnoczi
       header[0] != header_event_id or \
56 26f7227b Stefan Hajnoczi
       header[1] != header_magic or \
57 26f7227b Stefan Hajnoczi
       header[2] != header_version:
58 59da6684 Stefan Hajnoczi
        raise ValueError('not a trace file or incompatible version')
59 26f7227b Stefan Hajnoczi
60 26f7227b Stefan Hajnoczi
    while True:
61 26f7227b Stefan Hajnoczi
        rec = read_record(fobj)
62 26f7227b Stefan Hajnoczi
        if rec is None:
63 26f7227b Stefan Hajnoczi
            break
64 26f7227b Stefan Hajnoczi
65 26f7227b Stefan Hajnoczi
        yield rec
66 26f7227b Stefan Hajnoczi
67 59da6684 Stefan Hajnoczi
class Analyzer(object):
68 59da6684 Stefan Hajnoczi
    """A trace file analyzer which processes trace records.
69 59da6684 Stefan Hajnoczi

70 59da6684 Stefan Hajnoczi
    An analyzer can be passed to run() or process().  The begin() method is
71 59da6684 Stefan Hajnoczi
    invoked, then each trace record is processed, and finally the end() method
72 59da6684 Stefan Hajnoczi
    is invoked.
73 59da6684 Stefan Hajnoczi

74 59da6684 Stefan Hajnoczi
    If a method matching a trace event name exists, it is invoked to process
75 59da6684 Stefan Hajnoczi
    that trace record.  Otherwise the catchall() method is invoked."""
76 59da6684 Stefan Hajnoczi
77 59da6684 Stefan Hajnoczi
    def begin(self):
78 59da6684 Stefan Hajnoczi
        """Called at the start of the trace."""
79 59da6684 Stefan Hajnoczi
        pass
80 59da6684 Stefan Hajnoczi
81 59da6684 Stefan Hajnoczi
    def catchall(self, event, rec):
82 59da6684 Stefan Hajnoczi
        """Called if no specific method for processing a trace event has been found."""
83 59da6684 Stefan Hajnoczi
        pass
84 59da6684 Stefan Hajnoczi
85 59da6684 Stefan Hajnoczi
    def end(self):
86 59da6684 Stefan Hajnoczi
        """Called at the end of the trace."""
87 59da6684 Stefan Hajnoczi
        pass
88 59da6684 Stefan Hajnoczi
89 59da6684 Stefan Hajnoczi
def process(events, log, analyzer):
90 59da6684 Stefan Hajnoczi
    """Invoke an analyzer on each event in a log."""
91 59da6684 Stefan Hajnoczi
    if isinstance(events, str):
92 59da6684 Stefan Hajnoczi
        events = parse_events(open(events, 'r'))
93 59da6684 Stefan Hajnoczi
    if isinstance(log, str):
94 59da6684 Stefan Hajnoczi
        log = open(log, 'rb')
95 59da6684 Stefan Hajnoczi
96 59da6684 Stefan Hajnoczi
    def build_fn(analyzer, event):
97 59da6684 Stefan Hajnoczi
        fn = getattr(analyzer, event[0], None)
98 59da6684 Stefan Hajnoczi
        if fn is None:
99 59da6684 Stefan Hajnoczi
            return analyzer.catchall
100 59da6684 Stefan Hajnoczi
101 59da6684 Stefan Hajnoczi
        event_argcount = len(event) - 1
102 59da6684 Stefan Hajnoczi
        fn_argcount = len(inspect.getargspec(fn)[0]) - 1
103 59da6684 Stefan Hajnoczi
        if fn_argcount == event_argcount + 1:
104 59da6684 Stefan Hajnoczi
            # Include timestamp as first argument
105 59da6684 Stefan Hajnoczi
            return lambda _, rec: fn(*rec[1:2 + fn_argcount])
106 59da6684 Stefan Hajnoczi
        else:
107 59da6684 Stefan Hajnoczi
            # Just arguments, no timestamp
108 59da6684 Stefan Hajnoczi
            return lambda _, rec: fn(*rec[2:2 + fn_argcount])
109 59da6684 Stefan Hajnoczi
110 59da6684 Stefan Hajnoczi
    analyzer.begin()
111 59da6684 Stefan Hajnoczi
    fn_cache = {}
112 59da6684 Stefan Hajnoczi
    for rec in read_trace_file(log):
113 59da6684 Stefan Hajnoczi
        event_num = rec[0]
114 59da6684 Stefan Hajnoczi
        event = events[event_num]
115 59da6684 Stefan Hajnoczi
        if event_num not in fn_cache:
116 59da6684 Stefan Hajnoczi
            fn_cache[event_num] = build_fn(analyzer, event)
117 59da6684 Stefan Hajnoczi
        fn_cache[event_num](event, rec)
118 59da6684 Stefan Hajnoczi
    analyzer.end()
119 59da6684 Stefan Hajnoczi
120 59da6684 Stefan Hajnoczi
def run(analyzer):
121 59da6684 Stefan Hajnoczi
    """Execute an analyzer on a trace file given on the command-line.
122 59da6684 Stefan Hajnoczi

123 59da6684 Stefan Hajnoczi
    This function is useful as a driver for simple analysis scripts.  More
124 59da6684 Stefan Hajnoczi
    advanced scripts will want to call process() instead."""
125 59da6684 Stefan Hajnoczi
    import sys
126 59da6684 Stefan Hajnoczi
127 59da6684 Stefan Hajnoczi
    if len(sys.argv) != 3:
128 59da6684 Stefan Hajnoczi
        sys.stderr.write('usage: %s <trace-events> <trace-file>\n' % sys.argv[0])
129 59da6684 Stefan Hajnoczi
        sys.exit(1)
130 59da6684 Stefan Hajnoczi
131 59da6684 Stefan Hajnoczi
    events = parse_events(open(sys.argv[1], 'r'))
132 59da6684 Stefan Hajnoczi
    process(events, sys.argv[2], analyzer)
133 59da6684 Stefan Hajnoczi
134 59da6684 Stefan Hajnoczi
if __name__ == '__main__':
135 59da6684 Stefan Hajnoczi
    class Formatter(Analyzer):
136 59da6684 Stefan Hajnoczi
        def __init__(self):
137 59da6684 Stefan Hajnoczi
            self.last_timestamp = None
138 59da6684 Stefan Hajnoczi
139 59da6684 Stefan Hajnoczi
        def catchall(self, event, rec):
140 59da6684 Stefan Hajnoczi
            timestamp = rec[1]
141 59da6684 Stefan Hajnoczi
            if self.last_timestamp is None:
142 59da6684 Stefan Hajnoczi
                self.last_timestamp = timestamp
143 59da6684 Stefan Hajnoczi
            delta_ns = timestamp - self.last_timestamp
144 59da6684 Stefan Hajnoczi
            self.last_timestamp = timestamp
145 59da6684 Stefan Hajnoczi
146 59da6684 Stefan Hajnoczi
            fields = [event[0], '%0.3f' % (delta_ns / 1000.0)]
147 59da6684 Stefan Hajnoczi
            for i in xrange(1, len(event)):
148 59da6684 Stefan Hajnoczi
                fields.append('%s=0x%x' % (event[i], rec[i + 1]))
149 59da6684 Stefan Hajnoczi
            print ' '.join(fields)
150 59da6684 Stefan Hajnoczi
151 59da6684 Stefan Hajnoczi
    run(Formatter())