2 #############################################################################
4 # Copyright 2008 Tungsten Graphics, Inc.
6 # This program is free software: you can redistribute it and/or modify it
7 # under the terms of the GNU Lesser General Public License as published
8 # by the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #############################################################################
24 import xml.parsers.expat
27 ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4)
32 def __init__(self, type, name_or_data, attrs = None, line = None, column = None):
33 assert type in (ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF)
35 self.name_or_data = name_or_data
41 if self.type == ELEMENT_START:
42 return '<' + self.name_or_data + ' ...>'
43 if self.type == ELEMENT_END:
44 return '</' + self.name_or_data + '>'
45 if self.type == CHARACTER_DATA:
46 return self.name_or_data
53 """Expat based XML tokenizer."""
55 def __init__(self, fp, skip_ws = True):
60 self.skip_ws = skip_ws
62 self.character_pos = 0, 0
63 self.character_data = ''
65 self.parser = xml.parsers.expat.ParserCreate()
66 self.parser.StartElementHandler = self.handle_element_start
67 self.parser.EndElementHandler = self.handle_element_end
68 self.parser.CharacterDataHandler = self.handle_character_data
70 def handle_element_start(self, name, attributes):
71 self.finish_character_data()
72 line, column = self.pos()
73 token = XmlToken(ELEMENT_START, name, attributes, line, column)
74 self.tokens.append(token)
76 def handle_element_end(self, name):
77 self.finish_character_data()
78 line, column = self.pos()
79 token = XmlToken(ELEMENT_END, name, None, line, column)
80 self.tokens.append(token)
82 def handle_character_data(self, data):
83 if not self.character_data:
84 self.character_pos = self.pos()
85 self.character_data += data
87 def finish_character_data(self):
88 if self.character_data:
89 if not self.skip_ws or not self.character_data.isspace():
90 line, column = self.character_pos
91 token = XmlToken(CHARACTER_DATA, self.character_data, None, line, column)
92 self.tokens.append(token)
93 self.character_data = ''
97 while self.index >= len(self.tokens) and not self.final:
100 data = self.fp.read(size)
101 self.final = len(data) < size
103 self.parser.Parse(data, self.final)
104 except xml.parsers.expat.ExpatError, e:
105 #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS:
110 if self.index >= len(self.tokens):
111 line, column = self.pos()
112 token = XmlToken(EOF, None, None, line, column)
114 token = self.tokens[self.index]
119 return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber
122 class TokenMismatch(Exception):
124 def __init__(self, expected, found):
125 self.expected = expected
129 return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found))
134 """Base XML document parser."""
136 def __init__(self, fp):
137 self.tokenizer = XmlTokenizer(fp)
141 self.token = self.tokenizer.next()
143 def match_element_start(self, name):
144 return self.token.type == ELEMENT_START and self.token.name_or_data == name
146 def match_element_end(self, name):
147 return self.token.type == ELEMENT_END and self.token.name_or_data == name
149 def element_start(self, name):
150 while self.token.type == CHARACTER_DATA:
152 if self.token.type != ELEMENT_START:
153 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
154 if self.token.name_or_data != name:
155 raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)
156 attrs = self.token.attrs
160 def element_end(self, name):
161 while self.token.type == CHARACTER_DATA:
163 if self.token.type != ELEMENT_END:
164 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
165 if self.token.name_or_data != name:
166 raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)
169 def character_data(self, strip = True):
171 while self.token.type == CHARACTER_DATA:
172 data += self.token.name_or_data
181 def function(self, name):
184 def variable(self, name):
187 def literal(self, value):
190 def address(self, addr):
194 class AnsiFormatter(Formatter):
195 '''Formatter for plain-text files which outputs ANSI escape codes. See
196 http://en.wikipedia.org/wiki/ANSI_escape_code for more information
197 concerning ANSI escape codes.
209 def _escape(self, code, text):
210 return self._csi + code + text + self._csi + self._normal
212 def function(self, name):
213 text = Formatter.function(self, name)
214 return self._escape(self._bold, text)
216 def variable(self, name):
217 text = Formatter.variable(self, name)
218 return self._escape(self._italic, text)
220 def literal(self, value):
221 text = Formatter.literal(self, value)
222 return self._escape(self._blue, text)
224 def address(self, value):
225 text = Formatter.address(self, value)
226 return self._escape(self._green, text)
229 class TraceParser(XmlParser):
231 def __init__(self, stream, formatter):
232 XmlParser.__init__(self, stream)
233 self.formatter = formatter
236 self.element_start('trace')
237 while self.token.type not in (ELEMENT_END, EOF):
239 if self.token.type != EOF:
240 self.element_end('trace')
242 def parse_call(self):
243 attrs = self.element_start('call')
247 while self.token.type == ELEMENT_START:
248 if self.token.name_or_data == 'arg':
249 arg = self.parse_arg()
251 elif self.token.name_or_data == 'ret':
252 ret = self.parse_ret()
253 elif self.token.name_or_data == 'call':
254 # ignore nested function calls
257 raise TokenMismatch("<arg ...> or <ret ...>", self.token)
258 self.element_end('call')
260 call = self.formatter.function(name)
261 call += '(' + ', '.join([self.formatter.variable(name) + ' = ' + value for name, value in args]) + ')'
267 sys.stdout.write(call)
273 attrs = self.element_start('arg')
275 value = self.parse_value()
276 self.element_end('arg')
281 attrs = self.element_start('ret')
282 value = self.parse_value()
283 self.element_end('ret')
287 def parse_value(self):
288 if self.token.type == CHARACTER_DATA:
289 return self.formatter.literal(self.character_data())
290 if self.token.type == ELEMENT_START:
291 if self.token.name_or_data == 'elem':
292 return self.parse_elems()
293 if self.token.name_or_data == 'ref':
294 return self.parse_ref()
295 raise TokenMismatch("<elem ...>, <ref ...>, or text", self.token)
297 def parse_elems(self):
298 elems = [self.parse_elem()]
299 while self.token.type != ELEMENT_END:
300 elems.append(self.parse_elem())
301 return '{' + ', '.join([name + ' = ' + value for name, value in elems]) + '}'
303 def parse_elem(self):
304 attrs = self.element_start('elem')
306 value = self.parse_value()
307 self.element_end('elem')
312 attrs = self.element_start('ref')
313 if self.token.type != ELEMENT_END:
314 value = '&' + self.parse_value()
316 value = self.formatter.address(attrs['addr'])
317 self.element_end('ref')
323 parser = optparse.OptionParser(
324 usage="\n\t%prog [options] [file] ...")
326 '--color', '--colour',
327 type="choice", choices=('never', 'always', 'auto'), metavar='WHEN',
328 dest="color", default="always",
329 help="coloring: never, always, or auto [default: %default]")
330 (options, args) = parser.parse_args(sys.argv[1:])
332 if options.color == 'always' or options.color == 'auto' and sys.stdout.isatty():
333 formatter = AnsiFormatter()
335 formatter = Formatter()
339 if arg.endswith('.gz'):
340 from gzip import GzipFile
341 stream = GzipFile(arg, 'rt')
342 elif arg.endswith('.bz2'):
343 from bz2 import BZ2File
344 stream = BZ2File(arg, 'rt')
346 stream = open(arg, 'rt')
347 parser = TraceParser(stream, formatter)
350 parser = TraceParser(sys.stdin, formatter)
354 if __name__ == '__main__':