Path: blob/21.2-virgl/src/gallium/tools/trace/parse.py
4561 views
#!/usr/bin/env python31##########################################################################2#3# Copyright 2008 VMware, Inc.4# All Rights Reserved.5#6# Permission is hereby granted, free of charge, to any person obtaining a7# copy of this software and associated documentation files (the8# "Software"), to deal in the Software without restriction, including9# without limitation the rights to use, copy, modify, merge, publish,10# distribute, sub license, and/or sell copies of the Software, and to11# permit persons to whom the Software is furnished to do so, subject to12# the following conditions:13#14# The above copyright notice and this permission notice (including the15# next paragraph) shall be included in all copies or substantial portions16# of the Software.17#18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS19# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF20# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.21# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR22# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,23# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE24# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.25#26##########################################################################272829import io30import sys31import xml.parsers.expat as xpat32import argparse3334import format35from model import *363738ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4)394041class XmlToken:4243def __init__(self, type, name_or_data, attrs = None, line = None, column = None):44assert type in (ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF)45self.type = type46self.name_or_data = name_or_data47self.attrs = attrs48self.line = line49self.column = column5051def __str__(self):52if self.type == ELEMENT_START:53return '<' + self.name_or_data + ' ...>'54if self.type == ELEMENT_END:55return '</' + self.name_or_data + '>'56if self.type == CHARACTER_DATA:57return self.name_or_data58if self.type == EOF:59return 'end of file'60assert 0616263class XmlTokenizer:64"""Expat based XML tokenizer."""6566def __init__(self, fp, skip_ws = True):67self.fp = fp68self.tokens = []69self.index = 070self.final = False71self.skip_ws = skip_ws7273self.character_pos = 0, 074self.character_data = ''7576self.parser = xpat.ParserCreate()77self.parser.StartElementHandler = self.handle_element_start78self.parser.EndElementHandler = self.handle_element_end79self.parser.CharacterDataHandler = self.handle_character_data8081def handle_element_start(self, name, attributes):82self.finish_character_data()83line, column = self.pos()84token = XmlToken(ELEMENT_START, name, attributes, line, column)85self.tokens.append(token)8687def handle_element_end(self, name):88self.finish_character_data()89line, column = self.pos()90token = XmlToken(ELEMENT_END, name, None, line, column)91self.tokens.append(token)9293def handle_character_data(self, data):94if not self.character_data:95self.character_pos = self.pos()96self.character_data += data9798def finish_character_data(self):99if self.character_data:100if not self.skip_ws or not self.character_data.isspace():101line, column = self.character_pos102token = XmlToken(CHARACTER_DATA, self.character_data, None, line, column)103self.tokens.append(token)104self.character_data = ''105106def next(self):107size = 16*1024108while self.index >= len(self.tokens) and not self.final:109self.tokens = []110self.index = 0111data = self.fp.read(size)112self.final = len(data) < size113data = data.rstrip('\0')114try:115self.parser.Parse(data, self.final)116except xpat.ExpatError as e:117#if e.code == xpat.errors.XML_ERROR_NO_ELEMENTS:118if e.code == 3:119pass120else:121raise e122if self.index >= len(self.tokens):123line, column = self.pos()124token = XmlToken(EOF, None, None, line, column)125else:126token = self.tokens[self.index]127self.index += 1128return token129130def pos(self):131return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber132133134class TokenMismatch(Exception):135136def __init__(self, expected, found):137self.expected = expected138self.found = found139140def __str__(self):141return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found))142143144145class XmlParser:146"""Base XML document parser."""147148def __init__(self, fp):149self.tokenizer = XmlTokenizer(fp)150self.consume()151152def consume(self):153self.token = self.tokenizer.next()154155def match_element_start(self, name):156return self.token.type == ELEMENT_START and self.token.name_or_data == name157158def match_element_end(self, name):159return self.token.type == ELEMENT_END and self.token.name_or_data == name160161def element_start(self, name):162while self.token.type == CHARACTER_DATA:163self.consume()164if self.token.type != ELEMENT_START:165raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)166if self.token.name_or_data != name:167raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token)168attrs = self.token.attrs169self.consume()170return attrs171172def element_end(self, name):173while self.token.type == CHARACTER_DATA:174self.consume()175if self.token.type != ELEMENT_END:176raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)177if self.token.name_or_data != name:178raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token)179self.consume()180181def character_data(self, strip = True):182data = ''183while self.token.type == CHARACTER_DATA:184data += self.token.name_or_data185self.consume()186if strip:187data = data.strip()188return data189190191class TraceParser(XmlParser):192193def __init__(self, fp):194XmlParser.__init__(self, fp)195self.last_call_no = 0196197def parse(self):198self.element_start('trace')199while self.token.type not in (ELEMENT_END, EOF):200call = self.parse_call()201self.handle_call(call)202if self.token.type != EOF:203self.element_end('trace')204205def parse_call(self):206attrs = self.element_start('call')207try:208no = int(attrs['no'])209except KeyError as e:210self.last_call_no += 1211no = self.last_call_no212else:213self.last_call_no = no214klass = attrs['class']215method = attrs['method']216args = []217ret = None218time = None219while self.token.type == ELEMENT_START:220if self.token.name_or_data == 'arg':221arg = self.parse_arg()222args.append(arg)223elif self.token.name_or_data == 'ret':224ret = self.parse_ret()225elif self.token.name_or_data == 'call':226# ignore nested function calls227self.parse_call()228elif self.token.name_or_data == 'time':229time = self.parse_time()230else:231raise TokenMismatch("<arg ...> or <ret ...>", self.token)232self.element_end('call')233234return Call(no, klass, method, args, ret, time)235236def parse_arg(self):237attrs = self.element_start('arg')238name = attrs['name']239value = self.parse_value(name)240self.element_end('arg')241242return name, value243244def parse_ret(self):245attrs = self.element_start('ret')246value = self.parse_value('ret')247self.element_end('ret')248249return value250251def parse_time(self):252attrs = self.element_start('time')253time = self.parse_value('time');254self.element_end('time')255return time256257def parse_value(self, name):258expected_tokens = ('null', 'bool', 'int', 'uint', 'float', 'string', 'enum', 'array', 'struct', 'ptr', 'bytes')259if self.token.type == ELEMENT_START:260if self.token.name_or_data in expected_tokens:261method = getattr(self, 'parse_' + self.token.name_or_data)262return method(name)263raise TokenMismatch(" or " .join(expected_tokens), self.token)264265def parse_null(self, pname):266self.element_start('null')267self.element_end('null')268return Literal(None)269270def parse_bool(self, pname):271self.element_start('bool')272value = int(self.character_data())273self.element_end('bool')274return Literal(value)275276def parse_int(self, pname):277self.element_start('int')278value = int(self.character_data())279self.element_end('int')280return Literal(value)281282def parse_uint(self, pname):283self.element_start('uint')284value = int(self.character_data())285self.element_end('uint')286return Literal(value)287288def parse_float(self, pname):289self.element_start('float')290value = float(self.character_data())291self.element_end('float')292return Literal(value)293294def parse_enum(self, pname):295self.element_start('enum')296name = self.character_data()297self.element_end('enum')298return NamedConstant(name)299300def parse_string(self, pname):301self.element_start('string')302value = self.character_data()303self.element_end('string')304return Literal(value)305306def parse_bytes(self, pname):307self.element_start('bytes')308value = self.character_data()309self.element_end('bytes')310return Blob(value)311312def parse_array(self, pname):313self.element_start('array')314elems = []315while self.token.type != ELEMENT_END:316elems.append(self.parse_elem('array'))317self.element_end('array')318return Array(elems)319320def parse_elem(self, pname):321self.element_start('elem')322value = self.parse_value('elem')323self.element_end('elem')324return value325326def parse_struct(self, pname):327attrs = self.element_start('struct')328name = attrs['name']329members = []330while self.token.type != ELEMENT_END:331members.append(self.parse_member(name))332self.element_end('struct')333return Struct(name, members)334335def parse_member(self, pname):336attrs = self.element_start('member')337name = attrs['name']338value = self.parse_value(name)339self.element_end('member')340341return name, value342343def parse_ptr(self, pname):344self.element_start('ptr')345address = self.character_data()346self.element_end('ptr')347348return Pointer(address, pname)349350def handle_call(self, call):351pass352353354class SimpleTraceDumper(TraceParser):355356def __init__(self, fp, options, formatter):357TraceParser.__init__(self, fp)358self.options = options359self.formatter = formatter360self.pretty_printer = PrettyPrinter(self.formatter, options)361362def handle_call(self, call):363call.visit(self.pretty_printer)364self.formatter.newline()365366367class TraceDumper(SimpleTraceDumper):368369def __init__(self, fp, options, formatter):370SimpleTraceDumper.__init__(self, fp, options, formatter)371self.call_stack = []372373def handle_call(self, call):374if self.options.named_ptrs:375self.call_stack.append(call)376else:377call.visit(self.pretty_printer)378self.formatter.newline()379380def dump_calls(self):381for call in self.call_stack:382call.visit(self.pretty_printer)383self.formatter.newline()384385386class Main:387'''Common main class for all retrace command line utilities.'''388389def __init__(self):390pass391392def main(self):393optparser = self.get_optparser()394args = optparser.parse_args()395396for fname in args.filename:397try:398if fname.endswith('.gz'):399from gzip import GzipFile400stream = io.TextIOWrapper(GzipFile(fname, 'rb'))401elif fname.endswith('.bz2'):402from bz2 import BZ2File403stream = io.TextIOWrapper(BZ2File(fname, 'rb'))404else:405stream = open(fname, 'rt')406except Exception as e:407print("ERROR: {}".format(str(e)))408sys.exit(1)409410self.process_arg(stream, args)411412def get_optparser(self):413optparser = argparse.ArgumentParser(414description="Parse and dump Gallium trace(s)")415optparser.add_argument("filename", action="extend", nargs="+",416type=str, metavar="filename", help="Gallium trace filename (plain or .gz, .bz2)")417optparser.add_argument("-p", "--plain",418action="store_const", const=True, default=False,419dest="plain", help="disable ANSI color etc. formatting")420optparser.add_argument("-S", "--suppress",421action="store_const", const=True, default=False,422dest="suppress_variants", help="suppress some variants in output for better diffability")423optparser.add_argument("-N", "--named",424action="store_const", const=True, default=False,425dest="named_ptrs", help="generate symbolic names for raw pointer values")426optparser.add_argument("-M", "--method-only",427action="store_const", const=True, default=False,428dest="method_only", help="output only call names without arguments")429430return optparser431432def process_arg(self, stream, options):433if options.plain:434formatter = format.Formatter(sys.stdout)435else:436formatter = format.DefaultFormatter(sys.stdout)437438parser = TraceDumper(stream, options, formatter)439parser.parse()440441if options.named_ptrs:442parser.dump_calls()443444445if __name__ == '__main__':446Main().main()447448449