Statistics
| Branch: | Revision:

root / scripts / simpletrace.py @ 59da6684

History | View | Annotate | Download (4.5 kB)

1
#!/usr/bin/env python
2
#
3
# Pretty-printer for simple trace backend binary trace files
4
#
5
# Copyright IBM, Corp. 2010
6
#
7
# This work is licensed under the terms of the GNU GPL, version 2.  See
8
# the COPYING file in the top-level directory.
9
#
10
# For help see docs/tracing.txt
11

    
12
import struct
13
import re
14
import inspect
15

    
16
header_event_id = 0xffffffffffffffff
17
header_magic    = 0xf2b177cb0aa429b4
18
header_version  = 0
19

    
20
trace_fmt = '=QQQQQQQQ'
21
trace_len = struct.calcsize(trace_fmt)
22
event_re  = re.compile(r'(disable\s+)?([a-zA-Z0-9_]+)\(([^)]*)\).*')
23

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

    
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(','))
30

    
31
    events = {}
32
    event_num = 0
33
    for line in fobj:
34
        m = event_re.match(line.strip())
35
        if m is None:
36
            continue
37

    
38
        disable, name, args = m.groups()
39
        events[event_num] = (name,) + get_argnames(args)
40
        event_num += 1
41
    return events
42

    
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:
47
        return None
48
    return struct.unpack(trace_fmt, s)
49

    
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')
58

    
59
    while True:
60
        rec = read_record(fobj)
61
        if rec is None:
62
            break
63

    
64
        yield rec
65

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