Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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