Statistics
| Branch: | Revision:

root / scripts / simpletrace.py @ f53ec699

History | View | Annotate | Download (5.8 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
from tracetool import _read_events, Event
16
from tracetool.backend.simple import is_string
17

    
18
header_event_id = 0xffffffffffffffff
19
header_magic    = 0xf2b177cb0aa429b4
20
dropped_event_id = 0xfffffffffffffffe
21

    
22
log_header_fmt = '=QQQ'
23
rec_header_fmt = '=QQII'
24

    
25
def read_header(fobj, hfmt):
26
    '''Read a trace record header'''
27
    hlen = struct.calcsize(hfmt)
28
    hdr = fobj.read(hlen)
29
    if len(hdr) != hlen:
30
        return None
31
    return struct.unpack(hfmt, hdr)
32

    
33
def get_record(edict, rechdr, fobj):
34
    """Deserialize a trace record from a file into a tuple (event_num, timestamp, arg1, ..., arg6)."""
35
    if rechdr is None:
36
        return None
37
    rec = (rechdr[0], rechdr[1])
38
    if rechdr[0] != dropped_event_id:
39
        event_id = rechdr[0]
40
        event = edict[event_id]
41
        for type, name in event.args:
42
            if is_string(type):
43
                l = fobj.read(4)
44
                (len,) = struct.unpack('=L', l)
45
                s = fobj.read(len)
46
                rec = rec + (s,)
47
            else:
48
                (value,) = struct.unpack('=Q', fobj.read(8))
49
                rec = rec + (value,)
50
    else:
51
        (value,) = struct.unpack('=Q', fobj.read(8))
52
        rec = rec + (value,)
53
    return rec
54

    
55

    
56
def read_record(edict, fobj):
57
    """Deserialize a trace record from a file into a tuple (event_num, timestamp, arg1, ..., arg6)."""
58
    rechdr = read_header(fobj, rec_header_fmt)
59
    return get_record(edict, rechdr, fobj) # return tuple of record elements
60

    
61
def read_trace_file(edict, fobj):
62
    """Deserialize trace records from a file, yielding record tuples (event_num, timestamp, arg1, ..., arg6)."""
63
    header = read_header(fobj, log_header_fmt)
64
    if header is None or \
65
       header[0] != header_event_id or \
66
       header[1] != header_magic:
67
        raise ValueError('Not a valid trace file!')
68
    if header[2] != 0 and \
69
       header[2] != 2:
70
        raise ValueError('Unknown version of tracelog format!')
71

    
72
    log_version = header[2]
73
    if log_version == 0:
74
        raise ValueError('Older log format, not supported with this QEMU release!')
75

    
76
    while True:
77
        rec = read_record(edict, fobj)
78
        if rec is None:
79
            break
80

    
81
        yield rec
82

    
83
class Analyzer(object):
84
    """A trace file analyzer which processes trace records.
85

86
    An analyzer can be passed to run() or process().  The begin() method is
87
    invoked, then each trace record is processed, and finally the end() method
88
    is invoked.
89

90
    If a method matching a trace event name exists, it is invoked to process
91
    that trace record.  Otherwise the catchall() method is invoked."""
92

    
93
    def begin(self):
94
        """Called at the start of the trace."""
95
        pass
96

    
97
    def catchall(self, event, rec):
98
        """Called if no specific method for processing a trace event has been found."""
99
        pass
100

    
101
    def end(self):
102
        """Called at the end of the trace."""
103
        pass
104

    
105
def process(events, log, analyzer):
106
    """Invoke an analyzer on each event in a log."""
107
    if isinstance(events, str):
108
        events = _read_events(open(events, 'r'))
109
    if isinstance(log, str):
110
        log = open(log, 'rb')
111

    
112
    enabled_events = []
113
    dropped_event = Event.build("Dropped_Event(uint64_t num_events_dropped)")
114
    edict = {dropped_event_id: dropped_event}
115

    
116
    for e in events:
117
        if 'disable' not in e.properties:
118
            enabled_events.append(e)
119
    for num, event in enumerate(enabled_events):
120
        edict[num] = event
121

    
122
    def build_fn(analyzer, event):
123
        if isinstance(event, str):
124
            return analyzer.catchall
125

    
126
        fn = getattr(analyzer, event.name, None)
127
        if fn is None:
128
            return analyzer.catchall
129

    
130
        event_argcount = len(event.args)
131
        fn_argcount = len(inspect.getargspec(fn)[0]) - 1
132
        if fn_argcount == event_argcount + 1:
133
            # Include timestamp as first argument
134
            return lambda _, rec: fn(*rec[1:2 + event_argcount])
135
        else:
136
            # Just arguments, no timestamp
137
            return lambda _, rec: fn(*rec[2:2 + event_argcount])
138

    
139
    analyzer.begin()
140
    fn_cache = {}
141
    for rec in read_trace_file(edict, log):
142
        event_num = rec[0]
143
        event = edict[event_num]
144
        if event_num not in fn_cache:
145
            fn_cache[event_num] = build_fn(analyzer, event)
146
        fn_cache[event_num](event, rec)
147
    analyzer.end()
148

    
149
def run(analyzer):
150
    """Execute an analyzer on a trace file given on the command-line.
151

152
    This function is useful as a driver for simple analysis scripts.  More
153
    advanced scripts will want to call process() instead."""
154
    import sys
155

    
156
    if len(sys.argv) != 3:
157
        sys.stderr.write('usage: %s <trace-events> <trace-file>\n' % sys.argv[0])
158
        sys.exit(1)
159

    
160
    events = _read_events(open(sys.argv[1], 'r'))
161
    process(events, sys.argv[2], analyzer)
162

    
163
if __name__ == '__main__':
164
    class Formatter(Analyzer):
165
        def __init__(self):
166
            self.last_timestamp = None
167

    
168
        def catchall(self, event, rec):
169
            i = 1
170
            timestamp = rec[1]
171
            if self.last_timestamp is None:
172
                self.last_timestamp = timestamp
173
            delta_ns = timestamp - self.last_timestamp
174
            self.last_timestamp = timestamp
175

    
176
            fields = [event.name, '%0.3f' % (delta_ns / 1000.0)]
177
            for type, name in event.args:
178
                if is_string(type):
179
                    fields.append('%s=%s' % (name, rec[i + 1]))
180
                else:
181
                    fields.append('%s=0x%x' % (name, rec[i + 1]))
182
                i += 1
183
            print ' '.join(fields)
184

    
185
    run(Formatter())