st/mesa: fix incorrect RowStride computation
[mesa.git] / progs / gallium / python / retrace / parse.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
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 TUNGSTEN GRAPHICS 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 sys
31 import xml.parsers.expat
32 import binascii
33 import optparse
34
35 from model import *
36
37
38 ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4)
39
40
41 class XmlToken:
42
43 def __init__(self, type, name_or_data, attrs = None, line = None, column = None):
44 assert type in (ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF)
45 self.type = type
46 self.name_or_data = name_or_data
47 self.attrs = attrs
48 self.line = line
49 self.column = column
50
51 def __str__(self):
52 if self.type == ELEMENT_START:
53 return '<' + self.name_or_data + ' ...>'
54 if self.type == ELEMENT_END:
55 return '</' + self.name_or_data + '>'
56 if self.type == CHARACTER_DATA:
57 return self.name_or_data
58 if self.type == EOF:
59 return 'end of file'
60 assert 0
61
62
63 class XmlTokenizer:
64 """Expat based XML tokenizer."""
65
66 def __init__(self, fp, skip_ws = True):
67 self.fp = fp
68 self.tokens = []
69 self.index = 0
70 self.final = False
71 self.skip_ws = skip_ws
72
73 self.character_pos = 0, 0
74 self.character_data = ''
75
76 self.parser = xml.parsers.expat.ParserCreate()
77 self.parser.StartElementHandler = self.handle_element_start
78 self.parser.EndElementHandler = self.handle_element_end
79 self.parser.CharacterDataHandler = self.handle_character_data
80
81 def handle_element_start(self, name, attributes):
82 self.finish_character_data()
83 line, column = self.pos()
84 token = XmlToken(ELEMENT_START, name, attributes, line, column)
85 self.tokens.append(token)
86
87 def handle_element_end(self, name):
88 self.finish_character_data()
89 line, column = self.pos()
90 token = XmlToken(ELEMENT_END, name, None, line, column)
91 self.tokens.append(token)
92
93 def handle_character_data(self, data):
94 if not self.character_data:
95 self.character_pos = self.pos()
96 self.character_data += data
97
98 def finish_character_data(self):
99 if self.character_data:
100 if not self.skip_ws or not self.character_data.isspace():
101 line, column = self.character_pos
102 token = XmlToken(CHARACTER_DATA, self.character_data, None, line, column)
103 self.tokens.append(token)
104 self.character_data = ''
105
106 def next(self):
107 size = 16*1024
108 while self.index >= len(self.tokens) and not self.final:
109 self.tokens = []
110 self.index = 0
111 data = self.fp.read(size)
112 self.final = len(data) < size
113 data = data.rstrip('\0')
114 try:
115 self.parser.Parse(data, self.final)
116 except xml.parsers.expat.ExpatError, e:
117 #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS:
118 if e.code == 3:
119 pass
120 else:
121 raise e
122 if self.index >= len(self.tokens):
123 line, column = self.pos()
124 token = XmlToken(EOF, None, None, line, column)
125 else:
126 token = self.tokens[self.index]
127 self.index += 1
128 return token
129
130 def pos(self):
131 return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber
132
133
134 class TokenMismatch(Exception):
135
136 def __init__(self, expected, found):
137 self.expected = expected
138 self.found = found
139
140 def __str__(self):
141 return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found))
142
143
144
145 class XmlParser:
146 """Base XML document parser."""
147
148 def __init__(self, fp):
149 self.tokenizer = XmlTokenizer(fp)
150 self.consume()
151
152 def consume(self):
153 self.token = self.tokenizer.next()
154
155 def match_element_start(self, name):
156 return self.token.type == ELEMENT_START and self.token.name_or_data == name
157
158 def match_element_end(self, name):
159 return self.token.type == ELEMENT_END and self.token.name_or_data == name
160
161 def element_start(self, name):
162 while self.token.type == CHARACTER_DATA:
163 self.consume()
164 if self.token.type != ELEMENT_START:
165 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
166 if self.token.name_or_data != name:
167 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
168 attrs = self.token.attrs
169 self.consume()
170 return attrs
171
172 def element_end(self, name):
173 while self.token.type == CHARACTER_DATA:
174 self.consume()
175 if self.token.type != ELEMENT_END:
176 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
177 if self.token.name_or_data != name:
178 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
179 self.consume()
180
181 def character_data(self, strip = True):
182 data = ''
183 while self.token.type == CHARACTER_DATA:
184 data += self.token.name_or_data
185 self.consume()
186 if strip:
187 data = data.strip()
188 return data
189
190
191 class TraceParser(XmlParser):
192
193 def __init__(self, fp):
194 XmlParser.__init__(self, fp)
195 self.last_call_no = 0
196
197 def parse(self):
198 self.element_start('trace')
199 while self.token.type not in (ELEMENT_END, EOF):
200 call = self.parse_call()
201 self.handle_call(call)
202 if self.token.type != EOF:
203 self.element_end('trace')
204
205 def parse_call(self):
206 attrs = self.element_start('call')
207 try:
208 no = int(attrs['no'])
209 except KeyError:
210 self.last_call_no += 1
211 no = self.last_call_no
212 else:
213 self.last_call_no = no
214 klass = attrs['class']
215 method = attrs['method']
216 args = []
217 ret = None
218 while self.token.type == ELEMENT_START:
219 if self.token.name_or_data == 'arg':
220 arg = self.parse_arg()
221 args.append(arg)
222 elif self.token.name_or_data == 'ret':
223 ret = self.parse_ret()
224 elif self.token.name_or_data == 'call':
225 # ignore nested function calls
226 self.parse_call()
227 else:
228 raise TokenMismatch("<arg ...> or <ret ...>", self.token)
229 self.element_end('call')
230
231 return Call(no, klass, method, args, ret)
232
233 def parse_arg(self):
234 attrs = self.element_start('arg')
235 name = attrs['name']
236 value = self.parse_value()
237 self.element_end('arg')
238
239 return name, value
240
241 def parse_ret(self):
242 attrs = self.element_start('ret')
243 value = self.parse_value()
244 self.element_end('ret')
245
246 return value
247
248 def parse_value(self):
249 expected_tokens = ('null', 'bool', 'int', 'uint', 'float', 'string', 'enum', 'array', 'struct', 'ptr', 'bytes')
250 if self.token.type == ELEMENT_START:
251 if self.token.name_or_data in expected_tokens:
252 method = getattr(self, 'parse_' + self.token.name_or_data)
253 return method()
254 raise TokenMismatch(" or " .join(expected_tokens), self.token)
255
256 def parse_null(self):
257 self.element_start('null')
258 self.element_end('null')
259 return Literal(None)
260
261 def parse_bool(self):
262 self.element_start('bool')
263 value = int(self.character_data())
264 self.element_end('bool')
265 return Literal(value)
266
267 def parse_int(self):
268 self.element_start('int')
269 value = int(self.character_data())
270 self.element_end('int')
271 return Literal(value)
272
273 def parse_uint(self):
274 self.element_start('uint')
275 value = int(self.character_data())
276 self.element_end('uint')
277 return Literal(value)
278
279 def parse_float(self):
280 self.element_start('float')
281 value = float(self.character_data())
282 self.element_end('float')
283 return Literal(value)
284
285 def parse_enum(self):
286 self.element_start('enum')
287 name = self.character_data()
288 self.element_end('enum')
289 return NamedConstant(name)
290
291 def parse_string(self):
292 self.element_start('string')
293 value = self.character_data()
294 self.element_end('string')
295 return Literal(value)
296
297 def parse_bytes(self):
298 self.element_start('bytes')
299 value = binascii.a2b_hex(self.character_data())
300 self.element_end('bytes')
301 return Literal(value)
302
303 def parse_array(self):
304 self.element_start('array')
305 elems = []
306 while self.token.type != ELEMENT_END:
307 elems.append(self.parse_elem())
308 self.element_end('array')
309 return Array(elems)
310
311 def parse_elem(self):
312 self.element_start('elem')
313 value = self.parse_value()
314 self.element_end('elem')
315 return value
316
317 def parse_struct(self):
318 attrs = self.element_start('struct')
319 name = attrs['name']
320 members = []
321 while self.token.type != ELEMENT_END:
322 members.append(self.parse_member())
323 self.element_end('struct')
324 return Struct(name, members)
325
326 def parse_member(self):
327 attrs = self.element_start('member')
328 name = attrs['name']
329 value = self.parse_value()
330 self.element_end('member')
331
332 return name, value
333
334 def parse_ptr(self):
335 self.element_start('ptr')
336 address = self.character_data()
337 self.element_end('ptr')
338
339 return Pointer(address)
340
341 def handle_call(self, call):
342 pass
343
344
345 class TraceDumper(TraceParser):
346
347 def __init__(self, fp):
348 TraceParser.__init__(self, fp)
349 self.formatter = format.DefaultFormatter(sys.stdout)
350 self.pretty_printer = PrettyPrinter(self.formatter)
351
352 def handle_call(self, call):
353 call.visit(self.pretty_printer)
354 self.formatter.newline()
355
356
357 class Main:
358 '''Common main class for all retrace command line utilities.'''
359
360 def __init__(self):
361 pass
362
363 def main(self):
364 optparser = self.get_optparser()
365 (options, args) = optparser.parse_args(sys.argv[1:])
366
367 if args:
368 for arg in args:
369 if arg.endswith('.gz'):
370 from gzip import GzipFile
371 stream = GzipFile(arg, 'rt')
372 elif arg.endswith('.bz2'):
373 from bz2 import BZ2File
374 stream = BZ2File(arg, 'rU')
375 else:
376 stream = open(arg, 'rt')
377 self.process_arg(stream, options)
378 else:
379 self.process_arg(stream, options)
380
381 def get_optparser(self):
382 optparser = optparse.OptionParser(
383 usage="\n\t%prog [options] [traces] ...")
384 return optparser
385
386 def process_arg(self, stream, options):
387 parser = TraceDumper(stream)
388 parser.parse()
389
390
391 if __name__ == '__main__':
392 Main().main()