Revision 59da6684

b/scripts/simpletrace.py
9 9
#
10 10
# For help see docs/tracing.txt
11 11

  
12
import sys
13 12
import struct
14 13
import re
14
import inspect
15 15

  
16 16
header_event_id = 0xffffffffffffffff
17 17
header_magic    = 0xf2b177cb0aa429b4
......
21 21
trace_len = struct.calcsize(trace_fmt)
22 22
event_re  = re.compile(r'(disable\s+)?([a-zA-Z0-9_]+)\(([^)]*)\).*')
23 23

  
24
def err(msg):
25
    sys.stderr.write(msg + '\n')
26
    sys.exit(1)
27

  
28 24
def parse_events(fobj):
29
    """Parse a trace-events file."""
25
    """Parse a trace-events file into {event_num: (name, arg1, ...)}."""
30 26

  
31 27
    def get_argnames(args):
32 28
        """Extract argument names from a parameter list."""
......
45 41
    return events
46 42

  
47 43
def read_record(fobj):
48
    """Deserialize a trace record from a file."""
44
    """Deserialize a trace record from a file into a tuple (event_num, timestamp, arg1, ..., arg6)."""
49 45
    s = fobj.read(trace_len)
50 46
    if len(s) != trace_len:
51 47
        return None
52 48
    return struct.unpack(trace_fmt, s)
53 49

  
54 50
def read_trace_file(fobj):
55
    """Deserialize trace records from a file."""
51
    """Deserialize trace records from a file, yielding record tuples (event_num, timestamp, arg1, ..., arg6)."""
56 52
    header = read_record(fobj)
57 53
    if header is None or \
58 54
       header[0] != header_event_id or \
59 55
       header[1] != header_magic or \
60 56
       header[2] != header_version:
61
        err('not a trace file or incompatible version')
57
        raise ValueError('not a trace file or incompatible version')
62 58

  
63 59
    while True:
64 60
        rec = read_record(fobj)
......
67 63

  
68 64
        yield rec
69 65

  
70
class Formatter(object):
71
    def __init__(self, events):
72
        self.events = events
73
        self.last_timestamp = None
74

  
75
    def format_record(self, rec):
76
        if self.last_timestamp is None:
77
            self.last_timestamp = rec[1]
78
        delta_ns = rec[1] - self.last_timestamp
79
        self.last_timestamp = rec[1]
80

  
81
        event = self.events[rec[0]]
82
        fields = [event[0], '%0.3f' % (delta_ns / 1000.0)]
83
        for i in xrange(1, len(event)):
84
            fields.append('%s=0x%x' % (event[i], rec[i + 1]))
85
        return ' '.join(fields)
86

  
87
if len(sys.argv) != 3:
88
    err('usage: %s <trace-events> <trace-file>' % sys.argv[0])
89

  
90
events = parse_events(open(sys.argv[1], 'r'))
91
formatter = Formatter(events)
92
for rec in read_trace_file(open(sys.argv[2], 'rb')):
93
    print formatter.format_record(rec)
66
class Analyzer(object):
67
    """A trace file analyzer which processes trace records.
68

  
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
71
    is invoked.
72

  
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."""
75

  
76
    def begin(self):
77
        """Called at the start of the trace."""
78
        pass
79

  
80
    def catchall(self, event, rec):
81
        """Called if no specific method for processing a trace event has been found."""
82
        pass
83

  
84
    def end(self):
85
        """Called at the end of the trace."""
86
        pass
87

  
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):
93
        log = open(log, 'rb')
94

  
95
    def build_fn(analyzer, event):
96
        fn = getattr(analyzer, event[0], None)
97
        if fn is None:
98
            return analyzer.catchall
99

  
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])
105
        else:
106
            # Just arguments, no timestamp
107
            return lambda _, rec: fn(*rec[2:2 + fn_argcount])
108

  
109
    analyzer.begin()
110
    fn_cache = {}
111
    for rec in read_trace_file(log):
112
        event_num = rec[0]
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)
117
    analyzer.end()
118

  
119
def run(analyzer):
120
    """Execute an analyzer on a trace file given on the command-line.
121

  
122
    This function is useful as a driver for simple analysis scripts.  More
123
    advanced scripts will want to call process() instead."""
124
    import sys
125

  
126
    if len(sys.argv) != 3:
127
        sys.stderr.write('usage: %s <trace-events> <trace-file>\n' % sys.argv[0])
128
        sys.exit(1)
129

  
130
    events = parse_events(open(sys.argv[1], 'r'))
131
    process(events, sys.argv[2], analyzer)
132

  
133
if __name__ == '__main__':
134
    class Formatter(Analyzer):
135
        def __init__(self):
136
            self.last_timestamp = None
137

  
138
        def catchall(self, event, rec):
139
            timestamp = rec[1]
140
            if self.last_timestamp is None:
141
                self.last_timestamp = timestamp
142
            delta_ns = timestamp - self.last_timestamp
143
            self.last_timestamp = timestamp
144

  
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)
149

  
150
    run(Formatter())

Also available in: Unified diff