blob: 20f002606614ce6f61e00bfe80cce86eba9ee05a [file] [log] [blame]
Philippe Mathieu-Daudé3d004a32020-01-30 17:32:25 +01001#!/usr/bin/env python3
Stefan Hajnoczi26f72272010-05-22 19:24:51 +01002#
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#
Philippe Mathieu-Daudé87e03312017-07-28 19:46:05 -030010# For help see docs/devel/tracing.txt
Stefan Hajnoczi26f72272010-05-22 19:24:51 +010011
Stefan Hajnoczi26f72272010-05-22 19:24:51 +010012import struct
Stefan Hajnoczi59da6682011-02-22 13:59:41 +000013import inspect
Daniel P. Berranged1b97bc2016-10-04 14:35:56 +010014from tracetool import read_events, Event
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053015from tracetool.backend.simple import is_string
Stefan Hajnoczi26f72272010-05-22 19:24:51 +010016
17header_event_id = 0xffffffffffffffff
18header_magic = 0xf2b177cb0aa429b4
Stefan Hajnoczi0b5538c2011-02-26 18:38:39 +000019dropped_event_id = 0xfffffffffffffffe
Stefan Hajnoczi26f72272010-05-22 19:24:51 +010020
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +010021record_type_mapping = 0
22record_type_event = 1
23
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053024log_header_fmt = '=QQQ'
25rec_header_fmt = '=QQII'
Stefan Hajnoczi26f72272010-05-22 19:24:51 +010026
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053027def read_header(fobj, hfmt):
28 '''Read a trace record header'''
29 hlen = struct.calcsize(hfmt)
30 hdr = fobj.read(hlen)
31 if len(hdr) != hlen:
Stefan Hajnoczi26f72272010-05-22 19:24:51 +010032 return None
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053033 return struct.unpack(hfmt, hdr)
Stefan Hajnoczi26f72272010-05-22 19:24:51 +010034
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +010035def get_record(edict, idtoname, rechdr, fobj):
36 """Deserialize a trace record from a file into a tuple
37 (name, timestamp, pid, arg1, ..., arg6)."""
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053038 if rechdr is None:
39 return None
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053040 if rechdr[0] != dropped_event_id:
41 event_id = rechdr[0]
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +010042 name = idtoname[event_id]
43 rec = (name, rechdr[1], rechdr[3])
Jose Ricardo Ziviani249e9f72017-05-29 13:30:04 -030044 try:
45 event = edict[name]
Eduardo Habkostbd228082018-06-08 09:29:51 -030046 except KeyError as e:
Jose Ricardo Ziviani249e9f72017-05-29 13:30:04 -030047 import sys
48 sys.stderr.write('%s event is logged but is not declared ' \
49 'in the trace events file, try using ' \
50 'trace-events-all instead.\n' % str(e))
51 sys.exit(1)
52
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053053 for type, name in event.args:
54 if is_string(type):
55 l = fobj.read(4)
56 (len,) = struct.unpack('=L', l)
57 s = fobj.read(len)
58 rec = rec + (s,)
59 else:
60 (value,) = struct.unpack('=Q', fobj.read(8))
61 rec = rec + (value,)
62 else:
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +010063 rec = ("dropped", rechdr[1], rechdr[3])
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053064 (value,) = struct.unpack('=Q', fobj.read(8))
65 rec = rec + (value,)
66 return rec
67
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +010068def get_mapping(fobj):
69 (event_id, ) = struct.unpack('=Q', fobj.read(8))
70 (len, ) = struct.unpack('=L', fobj.read(4))
Eduardo Habkost749c1d82018-06-19 16:45:49 -030071 name = fobj.read(len).decode()
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053072
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +010073 return (event_id, name)
74
75def read_record(edict, idtoname, fobj):
Stefan Hajnoczi80ff35c2014-05-07 19:24:11 +020076 """Deserialize a trace record from a file into a tuple (event_num, timestamp, pid, arg1, ..., arg6)."""
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053077 rechdr = read_header(fobj, rec_header_fmt)
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +010078 return get_record(edict, idtoname, rechdr, fobj)
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053079
Stefan Hajnoczi15327c32014-06-22 21:46:06 +080080def read_trace_header(fobj):
81 """Read and verify trace file header"""
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053082 header = read_header(fobj, log_header_fmt)
Daniel P. Berrange25d54652017-01-25 16:14:17 +000083 if header is None:
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053084 raise ValueError('Not a valid trace file!')
Daniel P. Berrange25d54652017-01-25 16:14:17 +000085 if header[0] != header_event_id:
86 raise ValueError('Not a valid trace file, header id %d != %d' %
87 (header[0], header_event_id))
88 if header[1] != header_magic:
89 raise ValueError('Not a valid trace file, header magic %d != %d' %
90 (header[1], header_magic))
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +053091
92 log_version = header[2]
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +010093 if log_version not in [0, 2, 3, 4]:
Lluís Vilanovaef0bd3b2014-02-23 20:37:35 +010094 raise ValueError('Unknown version of tracelog format!')
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +010095 if log_version != 4:
Lluís Vilanovaef0bd3b2014-02-23 20:37:35 +010096 raise ValueError('Log format %d not supported with this QEMU release!'
97 % log_version)
Stefan Hajnoczi26f72272010-05-22 19:24:51 +010098
Stefan Hajnoczi840d8352017-08-15 09:44:30 +010099def read_trace_records(edict, idtoname, fobj):
100 """Deserialize trace records from a file, yielding record tuples (event_num, timestamp, pid, arg1, ..., arg6).
101
102 Note that `idtoname` is modified if the file contains mapping records.
103
104 Args:
105 edict (str -> Event): events dict, indexed by name
106 idtoname (int -> str): event names dict, indexed by event ID
107 fobj (file): input file
108
109 """
Stefan Hajnoczi26f72272010-05-22 19:24:51 +0100110 while True:
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +0100111 t = fobj.read(8)
112 if len(t) == 0:
Stefan Hajnoczi26f72272010-05-22 19:24:51 +0100113 break
114
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +0100115 (rectype, ) = struct.unpack('=Q', t)
116 if rectype == record_type_mapping:
117 event_id, name = get_mapping(fobj)
118 idtoname[event_id] = name
119 else:
120 rec = read_record(edict, idtoname, fobj)
121
122 yield rec
Stefan Hajnoczi26f72272010-05-22 19:24:51 +0100123
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000124class Analyzer(object):
125 """A trace file analyzer which processes trace records.
Stefan Hajnoczi26f72272010-05-22 19:24:51 +0100126
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000127 An analyzer can be passed to run() or process(). The begin() method is
128 invoked, then each trace record is processed, and finally the end() method
129 is invoked.
Stefan Hajnoczi26f72272010-05-22 19:24:51 +0100130
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000131 If a method matching a trace event name exists, it is invoked to process
Stefan Hajnoczi659370f2017-04-11 10:56:54 +0100132 that trace record. Otherwise the catchall() method is invoked.
133
134 Example:
135 The following method handles the runstate_set(int new_state) trace event::
136
137 def runstate_set(self, new_state):
138 ...
139
140 The method can also take a timestamp argument before the trace event
141 arguments::
142
143 def runstate_set(self, timestamp, new_state):
144 ...
145
146 Timestamps have the uint64_t type and are in nanoseconds.
147
148 The pid can be included in addition to the timestamp and is useful when
149 dealing with traces from multiple processes::
150
151 def runstate_set(self, timestamp, pid, new_state):
152 ...
153 """
Stefan Hajnoczi26f72272010-05-22 19:24:51 +0100154
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000155 def begin(self):
156 """Called at the start of the trace."""
157 pass
Stefan Hajnoczi26f72272010-05-22 19:24:51 +0100158
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000159 def catchall(self, event, rec):
160 """Called if no specific method for processing a trace event has been found."""
161 pass
162
163 def end(self):
164 """Called at the end of the trace."""
165 pass
166
Stefan Hajnoczi15327c32014-06-22 21:46:06 +0800167def process(events, log, analyzer, read_header=True):
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000168 """Invoke an analyzer on each event in a log."""
169 if isinstance(events, str):
Daniel P. Berrangé86b5aac2018-03-06 15:46:50 +0000170 events = read_events(open(events, 'r'), events)
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000171 if isinstance(log, str):
172 log = open(log, 'rb')
173
Stefan Hajnoczi15327c32014-06-22 21:46:06 +0800174 if read_header:
175 read_trace_header(log)
176
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +0530177 dropped_event = Event.build("Dropped_Event(uint64_t num_events_dropped)")
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +0100178 edict = {"dropped": dropped_event}
Stefan Hajnoczi840d8352017-08-15 09:44:30 +0100179 idtoname = {dropped_event_id: "dropped"}
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +0530180
Daniel P. Berrange7f1b5882016-10-04 14:35:50 +0100181 for event in events:
182 edict[event.name] = event
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +0530183
Stefan Hajnoczi840d8352017-08-15 09:44:30 +0100184 # If there is no header assume event ID mapping matches events list
185 if not read_header:
186 for event_id, event in enumerate(events):
187 idtoname[event_id] = event.name
188
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000189 def build_fn(analyzer, event):
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +0530190 if isinstance(event, str):
191 return analyzer.catchall
192
193 fn = getattr(analyzer, event.name, None)
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000194 if fn is None:
195 return analyzer.catchall
196
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +0530197 event_argcount = len(event.args)
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000198 fn_argcount = len(inspect.getargspec(fn)[0]) - 1
199 if fn_argcount == event_argcount + 1:
200 # Include timestamp as first argument
Stefan Hajnoczie42860a2018-02-22 16:39:01 +0000201 return lambda _, rec: fn(*(rec[1:2] + rec[3:3 + event_argcount]))
Stefan Hajnoczi80ff35c2014-05-07 19:24:11 +0200202 elif fn_argcount == event_argcount + 2:
203 # Include timestamp and pid
204 return lambda _, rec: fn(*rec[1:3 + event_argcount])
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000205 else:
Stefan Hajnoczi80ff35c2014-05-07 19:24:11 +0200206 # Just arguments, no timestamp or pid
207 return lambda _, rec: fn(*rec[3:3 + event_argcount])
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000208
209 analyzer.begin()
210 fn_cache = {}
Stefan Hajnoczi840d8352017-08-15 09:44:30 +0100211 for rec in read_trace_records(edict, idtoname, log):
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000212 event_num = rec[0]
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +0530213 event = edict[event_num]
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000214 if event_num not in fn_cache:
215 fn_cache[event_num] = build_fn(analyzer, event)
216 fn_cache[event_num](event, rec)
217 analyzer.end()
218
219def run(analyzer):
220 """Execute an analyzer on a trace file given on the command-line.
221
222 This function is useful as a driver for simple analysis scripts. More
223 advanced scripts will want to call process() instead."""
224 import sys
225
Stefan Hajnoczi15327c32014-06-22 21:46:06 +0800226 read_header = True
227 if len(sys.argv) == 4 and sys.argv[1] == '--no-header':
228 read_header = False
229 del sys.argv[1]
230 elif len(sys.argv) != 3:
231 sys.stderr.write('usage: %s [--no-header] <trace-events> ' \
232 '<trace-file>\n' % sys.argv[0])
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000233 sys.exit(1)
234
Daniel P. Berrangé86b5aac2018-03-06 15:46:50 +0000235 events = read_events(open(sys.argv[1], 'r'), sys.argv[1])
Stefan Hajnoczi15327c32014-06-22 21:46:06 +0800236 process(events, sys.argv[2], analyzer, read_header=read_header)
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000237
238if __name__ == '__main__':
239 class Formatter(Analyzer):
240 def __init__(self):
241 self.last_timestamp = None
242
243 def catchall(self, event, rec):
244 timestamp = rec[1]
245 if self.last_timestamp is None:
246 self.last_timestamp = timestamp
247 delta_ns = timestamp - self.last_timestamp
248 self.last_timestamp = timestamp
249
Stefan Hajnoczi80ff35c2014-05-07 19:24:11 +0200250 fields = [event.name, '%0.3f' % (delta_ns / 1000.0),
251 'pid=%d' % rec[2]]
252 i = 3
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +0530253 for type, name in event.args:
254 if is_string(type):
Stefan Hajnoczi80ff35c2014-05-07 19:24:11 +0200255 fields.append('%s=%s' % (name, rec[i]))
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +0530256 else:
Stefan Hajnoczi80ff35c2014-05-07 19:24:11 +0200257 fields.append('%s=0x%x' % (name, rec[i]))
Harsh Prateek Bora90a147a2012-07-18 15:16:00 +0530258 i += 1
Eduardo Habkostf03868b2018-06-08 09:29:43 -0300259 print(' '.join(fields))
Stefan Hajnoczi59da6682011-02-22 13:59:41 +0000260
261 run(Formatter())