Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/tools/trace/parse.py
4561 views
1
#!/usr/bin/env python3
2
##########################################################################
3
#
4
# Copyright 2008 VMware, Inc.
5
# All Rights Reserved.
6
#
7
# Permission is hereby granted, free of charge, to any person obtaining a
8
# copy of this software and associated documentation files (the
9
# "Software"), to deal in the Software without restriction, including
10
# without limitation the rights to use, copy, modify, merge, publish,
11
# distribute, sub license, and/or sell copies of the Software, and to
12
# permit persons to whom the Software is furnished to do so, subject to
13
# the following conditions:
14
#
15
# The above copyright notice and this permission notice (including the
16
# next paragraph) shall be included in all copies or substantial portions
17
# of the Software.
18
#
19
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22
# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
23
# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
#
27
##########################################################################
28
29
30
import io
31
import sys
32
import xml.parsers.expat as xpat
33
import argparse
34
35
import format
36
from model import *
37
38
39
ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4)
40
41
42
class XmlToken:
43
44
def __init__(self, type, name_or_data, attrs = None, line = None, column = None):
45
assert type in (ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF)
46
self.type = type
47
self.name_or_data = name_or_data
48
self.attrs = attrs
49
self.line = line
50
self.column = column
51
52
def __str__(self):
53
if self.type == ELEMENT_START:
54
return '<' + self.name_or_data + ' ...>'
55
if self.type == ELEMENT_END:
56
return '</' + self.name_or_data + '>'
57
if self.type == CHARACTER_DATA:
58
return self.name_or_data
59
if self.type == EOF:
60
return 'end of file'
61
assert 0
62
63
64
class XmlTokenizer:
65
"""Expat based XML tokenizer."""
66
67
def __init__(self, fp, skip_ws = True):
68
self.fp = fp
69
self.tokens = []
70
self.index = 0
71
self.final = False
72
self.skip_ws = skip_ws
73
74
self.character_pos = 0, 0
75
self.character_data = ''
76
77
self.parser = xpat.ParserCreate()
78
self.parser.StartElementHandler = self.handle_element_start
79
self.parser.EndElementHandler = self.handle_element_end
80
self.parser.CharacterDataHandler = self.handle_character_data
81
82
def handle_element_start(self, name, attributes):
83
self.finish_character_data()
84
line, column = self.pos()
85
token = XmlToken(ELEMENT_START, name, attributes, line, column)
86
self.tokens.append(token)
87
88
def handle_element_end(self, name):
89
self.finish_character_data()
90
line, column = self.pos()
91
token = XmlToken(ELEMENT_END, name, None, line, column)
92
self.tokens.append(token)
93
94
def handle_character_data(self, data):
95
if not self.character_data:
96
self.character_pos = self.pos()
97
self.character_data += data
98
99
def finish_character_data(self):
100
if self.character_data:
101
if not self.skip_ws or not self.character_data.isspace():
102
line, column = self.character_pos
103
token = XmlToken(CHARACTER_DATA, self.character_data, None, line, column)
104
self.tokens.append(token)
105
self.character_data = ''
106
107
def next(self):
108
size = 16*1024
109
while self.index >= len(self.tokens) and not self.final:
110
self.tokens = []
111
self.index = 0
112
data = self.fp.read(size)
113
self.final = len(data) < size
114
data = data.rstrip('\0')
115
try:
116
self.parser.Parse(data, self.final)
117
except xpat.ExpatError as e:
118
#if e.code == xpat.errors.XML_ERROR_NO_ELEMENTS:
119
if e.code == 3:
120
pass
121
else:
122
raise e
123
if self.index >= len(self.tokens):
124
line, column = self.pos()
125
token = XmlToken(EOF, None, None, line, column)
126
else:
127
token = self.tokens[self.index]
128
self.index += 1
129
return token
130
131
def pos(self):
132
return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber
133
134
135
class TokenMismatch(Exception):
136
137
def __init__(self, expected, found):
138
self.expected = expected
139
self.found = found
140
141
def __str__(self):
142
return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found))
143
144
145
146
class XmlParser:
147
"""Base XML document parser."""
148
149
def __init__(self, fp):
150
self.tokenizer = XmlTokenizer(fp)
151
self.consume()
152
153
def consume(self):
154
self.token = self.tokenizer.next()
155
156
def match_element_start(self, name):
157
return self.token.type == ELEMENT_START and self.token.name_or_data == name
158
159
def match_element_end(self, name):
160
return self.token.type == ELEMENT_END and self.token.name_or_data == name
161
162
def element_start(self, name):
163
while self.token.type == CHARACTER_DATA:
164
self.consume()
165
if self.token.type != ELEMENT_START:
166
raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
167
if self.token.name_or_data != name:
168
raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
169
attrs = self.token.attrs
170
self.consume()
171
return attrs
172
173
def element_end(self, name):
174
while self.token.type == CHARACTER_DATA:
175
self.consume()
176
if self.token.type != ELEMENT_END:
177
raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
178
if self.token.name_or_data != name:
179
raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
180
self.consume()
181
182
def character_data(self, strip = True):
183
data = ''
184
while self.token.type == CHARACTER_DATA:
185
data += self.token.name_or_data
186
self.consume()
187
if strip:
188
data = data.strip()
189
return data
190
191
192
class TraceParser(XmlParser):
193
194
def __init__(self, fp):
195
XmlParser.__init__(self, fp)
196
self.last_call_no = 0
197
198
def parse(self):
199
self.element_start('trace')
200
while self.token.type not in (ELEMENT_END, EOF):
201
call = self.parse_call()
202
self.handle_call(call)
203
if self.token.type != EOF:
204
self.element_end('trace')
205
206
def parse_call(self):
207
attrs = self.element_start('call')
208
try:
209
no = int(attrs['no'])
210
except KeyError as e:
211
self.last_call_no += 1
212
no = self.last_call_no
213
else:
214
self.last_call_no = no
215
klass = attrs['class']
216
method = attrs['method']
217
args = []
218
ret = None
219
time = None
220
while self.token.type == ELEMENT_START:
221
if self.token.name_or_data == 'arg':
222
arg = self.parse_arg()
223
args.append(arg)
224
elif self.token.name_or_data == 'ret':
225
ret = self.parse_ret()
226
elif self.token.name_or_data == 'call':
227
# ignore nested function calls
228
self.parse_call()
229
elif self.token.name_or_data == 'time':
230
time = self.parse_time()
231
else:
232
raise TokenMismatch("<arg ...> or <ret ...>", self.token)
233
self.element_end('call')
234
235
return Call(no, klass, method, args, ret, time)
236
237
def parse_arg(self):
238
attrs = self.element_start('arg')
239
name = attrs['name']
240
value = self.parse_value(name)
241
self.element_end('arg')
242
243
return name, value
244
245
def parse_ret(self):
246
attrs = self.element_start('ret')
247
value = self.parse_value('ret')
248
self.element_end('ret')
249
250
return value
251
252
def parse_time(self):
253
attrs = self.element_start('time')
254
time = self.parse_value('time');
255
self.element_end('time')
256
return time
257
258
def parse_value(self, name):
259
expected_tokens = ('null', 'bool', 'int', 'uint', 'float', 'string', 'enum', 'array', 'struct', 'ptr', 'bytes')
260
if self.token.type == ELEMENT_START:
261
if self.token.name_or_data in expected_tokens:
262
method = getattr(self, 'parse_' + self.token.name_or_data)
263
return method(name)
264
raise TokenMismatch(" or " .join(expected_tokens), self.token)
265
266
def parse_null(self, pname):
267
self.element_start('null')
268
self.element_end('null')
269
return Literal(None)
270
271
def parse_bool(self, pname):
272
self.element_start('bool')
273
value = int(self.character_data())
274
self.element_end('bool')
275
return Literal(value)
276
277
def parse_int(self, pname):
278
self.element_start('int')
279
value = int(self.character_data())
280
self.element_end('int')
281
return Literal(value)
282
283
def parse_uint(self, pname):
284
self.element_start('uint')
285
value = int(self.character_data())
286
self.element_end('uint')
287
return Literal(value)
288
289
def parse_float(self, pname):
290
self.element_start('float')
291
value = float(self.character_data())
292
self.element_end('float')
293
return Literal(value)
294
295
def parse_enum(self, pname):
296
self.element_start('enum')
297
name = self.character_data()
298
self.element_end('enum')
299
return NamedConstant(name)
300
301
def parse_string(self, pname):
302
self.element_start('string')
303
value = self.character_data()
304
self.element_end('string')
305
return Literal(value)
306
307
def parse_bytes(self, pname):
308
self.element_start('bytes')
309
value = self.character_data()
310
self.element_end('bytes')
311
return Blob(value)
312
313
def parse_array(self, pname):
314
self.element_start('array')
315
elems = []
316
while self.token.type != ELEMENT_END:
317
elems.append(self.parse_elem('array'))
318
self.element_end('array')
319
return Array(elems)
320
321
def parse_elem(self, pname):
322
self.element_start('elem')
323
value = self.parse_value('elem')
324
self.element_end('elem')
325
return value
326
327
def parse_struct(self, pname):
328
attrs = self.element_start('struct')
329
name = attrs['name']
330
members = []
331
while self.token.type != ELEMENT_END:
332
members.append(self.parse_member(name))
333
self.element_end('struct')
334
return Struct(name, members)
335
336
def parse_member(self, pname):
337
attrs = self.element_start('member')
338
name = attrs['name']
339
value = self.parse_value(name)
340
self.element_end('member')
341
342
return name, value
343
344
def parse_ptr(self, pname):
345
self.element_start('ptr')
346
address = self.character_data()
347
self.element_end('ptr')
348
349
return Pointer(address, pname)
350
351
def handle_call(self, call):
352
pass
353
354
355
class SimpleTraceDumper(TraceParser):
356
357
def __init__(self, fp, options, formatter):
358
TraceParser.__init__(self, fp)
359
self.options = options
360
self.formatter = formatter
361
self.pretty_printer = PrettyPrinter(self.formatter, options)
362
363
def handle_call(self, call):
364
call.visit(self.pretty_printer)
365
self.formatter.newline()
366
367
368
class TraceDumper(SimpleTraceDumper):
369
370
def __init__(self, fp, options, formatter):
371
SimpleTraceDumper.__init__(self, fp, options, formatter)
372
self.call_stack = []
373
374
def handle_call(self, call):
375
if self.options.named_ptrs:
376
self.call_stack.append(call)
377
else:
378
call.visit(self.pretty_printer)
379
self.formatter.newline()
380
381
def dump_calls(self):
382
for call in self.call_stack:
383
call.visit(self.pretty_printer)
384
self.formatter.newline()
385
386
387
class Main:
388
'''Common main class for all retrace command line utilities.'''
389
390
def __init__(self):
391
pass
392
393
def main(self):
394
optparser = self.get_optparser()
395
args = optparser.parse_args()
396
397
for fname in args.filename:
398
try:
399
if fname.endswith('.gz'):
400
from gzip import GzipFile
401
stream = io.TextIOWrapper(GzipFile(fname, 'rb'))
402
elif fname.endswith('.bz2'):
403
from bz2 import BZ2File
404
stream = io.TextIOWrapper(BZ2File(fname, 'rb'))
405
else:
406
stream = open(fname, 'rt')
407
except Exception as e:
408
print("ERROR: {}".format(str(e)))
409
sys.exit(1)
410
411
self.process_arg(stream, args)
412
413
def get_optparser(self):
414
optparser = argparse.ArgumentParser(
415
description="Parse and dump Gallium trace(s)")
416
optparser.add_argument("filename", action="extend", nargs="+",
417
type=str, metavar="filename", help="Gallium trace filename (plain or .gz, .bz2)")
418
optparser.add_argument("-p", "--plain",
419
action="store_const", const=True, default=False,
420
dest="plain", help="disable ANSI color etc. formatting")
421
optparser.add_argument("-S", "--suppress",
422
action="store_const", const=True, default=False,
423
dest="suppress_variants", help="suppress some variants in output for better diffability")
424
optparser.add_argument("-N", "--named",
425
action="store_const", const=True, default=False,
426
dest="named_ptrs", help="generate symbolic names for raw pointer values")
427
optparser.add_argument("-M", "--method-only",
428
action="store_const", const=True, default=False,
429
dest="method_only", help="output only call names without arguments")
430
431
return optparser
432
433
def process_arg(self, stream, options):
434
if options.plain:
435
formatter = format.Formatter(sys.stdout)
436
else:
437
formatter = format.DefaultFormatter(sys.stdout)
438
439
parser = TraceDumper(stream, options, formatter)
440
parser.parse()
441
442
if options.named_ptrs:
443
parser.dump_calls()
444
445
446
if __name__ == '__main__':
447
Main().main()
448
449