Tizen 2.0 Release
[profile/ivi/osmesa.git] / src / gallium / tools / trace / 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()