]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/tools/tsar/tsar
update
[l4.git] / l4 / pkg / plr / tools / tsar / tsar
1 #!/usr/bin/python
2 # -*- coding: iso-8859-1 -*-
3 #vi: ft=python
4 #
5 # TSAR - Trace Sequence AnalyzeR
6 #        *     *        *      *
7 # Tool to extract and analyze Romain event logs
8 #
9 # (c) 2012-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
10 #     economic rights: Technische Universität Dresden (Germany)
11 #
12 #  This file is part of TUD:OS and distributed under the terms of the
13 #  GNU General Public License 2.
14 #  Please see the COPYING-GPL-2 file for details.
15
16 import gzip
17 import operator
18 import os
19 import re
20 import sys
21 import struct
22 import tempfile
23 import uu
24
25 import tsar_events
26
27
28 class EventFactory:
29     """Event factory: random access to Event objects
30        based on an underlying trace reader."""
31
32     def __init__(self, traceReader):
33         self.input = traceReader.raw_read()
34         self.offset = 0
35         self.idpfx = traceReader.prefix()
36
37     def uid(self, utcb):
38         return "%s:%x" % (self.idpfx, utcb)
39
40     def reset(self):
41         self.offset = 0
42
43     def _build(self, start):
44         """Get the Event object in the input stream
45            at the given position"""
46
47         if start >= len(self.input):
48             return None
49
50         eventTypes = [
51             None,
52             tsar_events.SyscallEvent,
53             tsar_events.PagefaultEvent,
54             tsar_events.SwifiEvent,
55             tsar_events.FooEvent,
56             tsar_events.TrapEvent,
57             tsar_events.ThreadStartEvent,
58             tsar_events.ThreadStopEvent,
59         ]
60
61         #print "Input: %d bytes" % len(bytes)
62
63         (tsc, utcb, typ) = struct.unpack_from("QIB", self.input[start:])
64
65         try:
66             return eventTypes[typ](self.input[start:], tsc, utcb,
67                                    self.uid(utcb))
68         except IndexError:
69             print "Index error with event type %d" % typ
70             sys.exit(1)
71
72     def nextEvent(self):
73         """Get the next event in stream
74
75         Allows iterating over the stream."""
76         start = self.offset * tsar_events.Event.EVENTSIZE
77         self.offset += 1
78         return self._build(start)
79
80     def eventAtOffset(self, offset):
81         """Get event at a specified offset.
82
83         Random access to the stream."""
84         start = offset * tsar_events.Event.EVENTSIZE
85         return self._build(start)
86
87
88 class TraceReader:
89     """Reader for zipped uu-encoded data"""
90
91     def __init__(self, logfile):
92         tmp = tempfile.NamedTemporaryFile(mode="w+b")
93         # XXX also print UID here
94         print "UUDecoding %s -> %s" % (logfile.name, tmp.name)
95         uu.decode(logfile.name, tmp.name)
96         self.zipFile = gzip.open(tmp.name)
97
98         self.pfx = os.path.splitext(os.path.basename(logfile.name))[0]
99
100     def prefix(self):
101         return self.pfx
102
103     def raw_read(self):
104         """Read the raw bytes from the underlying stream
105            into a string."""
106         decomp = ""
107         bytes = self.zipFile.read(1024)
108         while bytes != "":
109             decomp += bytes
110             bytes = self.zipFile.read(1024)
111         print "Read %d bytes." % len(decomp)
112
113         return decomp
114
115
116 class EventList:
117     """
118     Raw list of events from potentially multiple event stream sources
119     """
120     def __init__(self):
121         self.streams = []
122         self.stream_index = 0
123
124     def addStream(self, eventFactory):
125         """Add another stream to the event sources"""
126         self.streams += [eventFactory]
127
128     def reset(self):
129         """Reset the event list"""
130         for s in self.streams:
131             s.reset()
132         self.stream_index = 0
133
134     def next(self):
135         """Return the next event.
136
137         Continuously calling this function iterates over the associated
138         streams one at a time. Returns 'None' if no more events are to
139         be found
140         """
141
142         if self.stream_index >= len(self.streams):
143             return None
144
145         ev = self.streams[self.stream_index].nextEvent()
146
147         if ev is None:
148             self.stream_index += 1
149             return self.next()
150
151         return ev
152
153
154 def print_plain(events):
155     """Plain dump of an event list"""
156     for e in events:
157         print e
158
159
160 def print_pretty(events):
161     """Pretty-printed event list.
162
163     Prints a table. First column is a time stamp. Every other
164     colunm represents one distinct replica
165     """
166     ids = []
167
168     # first run to determine UTCB IDs
169     for e in events:
170         if e.uid() not in ids:
171             ids += [e.uid()]
172     ids.sort()
173
174     # header
175     print "\033[32m%14s" % "Timestamp",
176     for i in ids:
177         print "| %20s" % ("UID %s" % i[0:16]),
178     print "\033[0m"
179
180     # printing run
181     for e in events:
182         first = True
183         for line in e.pretty():
184             if first:
185                 print "%14s" % e.ts,
186                 first = False
187             else:
188                 print " " * 14,
189
190             idx = ids.index(e.uid())
191
192             for i in range(0, idx):
193                 print "|",
194                 print " " * 20,
195             if e.type == tsar_events.Event.TRAP_TYPE and e.is_start:
196                 print "| \033[33m%20s\033[0m" % line,
197             else:
198                 print "| %20s" % line,
199             for i in range(idx + 1, len(ids)):
200                 print "|",
201                 print " " * 20,
202             print
203
204
205 def remove_vt100(line):
206     """Remoe any occurrence of a VT100 color sequence from a string"""
207     return re.sub("\033\[\d+(;\d+)?m", "", line)
208
209
210 def extractLogs(inputFile):
211     """Take a QEMU log file (Fiasco serial output) and extract the
212        UU-encoded trace dumps from it.
213     """
214     files = []         # list of trace files
215     inLogFile = False  # are we currently operating on a dump?
216
217     for l in file(inputFile).readlines():
218         v = remove_vt100(l)
219
220         # start and end indicators
221         startmo = re.match("romain.*(begin 644 .*.gz)", v)
222         endmo = re.match("romain.*end", v)
223
224         if startmo:
225             curFile = tempfile.NamedTemporaryFile(mode="w+")
226             inLogFile = True
227
228         # Write lines belonging to the dump to temporary file.
229         # Remove L4Re's log prefix upfront
230         if inLogFile:
231             curFile.write(v.replace("romain  | ", ""))
232
233         if inLogFile and endmo:
234             files += [curFile]
235             inLogFile = False
236
237     return files
238
239
240 def main():
241     if len(sys.argv) < 2:
242         print "Need at least 1 argument"
243         sys.exit(1)
244
245     elist = EventList()
246
247     for f in extractLogs(sys.argv[1]):
248         print "=== %s ===" % f.name
249         f.seek(0)  # need to reset file seek ptr
250         elist.addStream(EventFactory(TraceReader(f)))
251
252     events = []
253
254     e = elist.next()
255     while e is not None:
256         events += [e]
257         e = elist.next()
258
259     # events are not necessarily sorted by time right now!
260     events.sort(key=operator.attrgetter('ts'))
261
262     #print_plain(events)
263     print_pretty(events)
264
265
266 if __name__ == "__main__":
267     main()