blob: 06ccaefbfd80b34be9bf25fbaab528cfc6021f63 [file] [log] [blame] [edit]
#!/usr/bin/env python3
##########################################################################
#
# Copyright 2008 VMware, Inc.
# All Rights Reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sub license, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice (including the
# next paragraph) shall be included in all copies or substantial portions
# of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
##########################################################################
'''Trace data model.'''
import sys
import string
import binascii
from io import StringIO
import format
class ModelOptions:
def __init__(self, args=None):
# Initialize the options we need to exist,
# with some reasonable defaults
self.plain = False
self.suppress_variants = False
self.named_ptrs = False
self.method_only = False
# If args is specified, we assume it is the result object
# from ArgumentParser.parse_args(). Copy the attribute values
# we have from it, if they exist.
if args is not None:
for var in self.__dict__:
if var in args.__dict__:
self.__dict__[var] = args.__dict__[var]
class TraceStateData:
def __init__(self):
self.ptr_list = {}
self.ptr_type_list = {}
self.ptr_types_list = {}
class Node:
def visit(self, visitor):
raise NotImplementedError
def __str__(self):
stream = StringIO()
formatter = format.Formatter(stream)
pretty_printer = PrettyPrinter(formatter, {})
self.visit(pretty_printer)
return stream.getvalue()
def __hash__(self):
raise NotImplementedError
class Literal(Node):
def __init__(self, value):
self.value = value
def visit(self, visitor):
visitor.visit_literal(self)
def __hash__(self):
return hash(self.value)
class Blob(Node):
def __init__(self, value):
self.value = binascii.a2b_hex(value)
def getValue(self):
return self.value
def visit(self, visitor):
visitor.visit_blob(self)
def __hash__(self):
return hash(self.value)
class NamedConstant(Node):
def __init__(self, name):
self.name = name
def visit(self, visitor):
visitor.visit_named_constant(self)
def __hash__(self):
return hash(self.name)
class Array(Node):
def __init__(self, elements):
self.elements = elements
def visit(self, visitor):
visitor.visit_array(self)
def __hash__(self):
tmp = 0
for mobj in self.elements:
tmp = tmp ^ hash(mobj)
return tmp
class Struct(Node):
def __init__(self, name, members):
self.name = name
self.members = members
def visit(self, visitor):
visitor.visit_struct(self)
def __hash__(self):
tmp = hash(self.name)
for mname, mobj in self.members:
tmp = tmp ^ hash(mname) ^ hash(mobj)
return tmp
class Pointer(Node):
ptr_ignore_list = ["ret", "elem"]
def __init__(self, state, address, pname):
self.address = address
self.state = state
# Check if address exists in list and if it is a return value address
t1 = address in state.ptr_list
if t1:
rname = state.ptr_type_list[address]
t2 = rname in self.ptr_ignore_list and pname not in self.ptr_ignore_list
else:
rname = pname
t2 = False
# If address does NOT exist (add it), OR IS a ret value (update with new type)
if not t1 or t2:
# If previously set to ret value, remove one from count
if t1 and t2:
self.adjust_ptr_type_count(rname, -1)
# Add / update
self.adjust_ptr_type_count(pname, 1)
tmp = "{}_{}".format(pname, state.ptr_types_list[pname])
state.ptr_list[address] = tmp
state.ptr_type_list[address] = pname
def adjust_ptr_type_count(self, pname, delta):
if pname not in self.state.ptr_types_list:
self.state.ptr_types_list[pname] = 0
self.state.ptr_types_list[pname] += delta
def named_address(self):
return self.state.ptr_list[self.address]
def visit(self, visitor):
visitor.visit_pointer(self)
def __hash__(self):
return hash(self.named_address())
class Call:
def __init__(self, no, klass, method, args, ret, time):
self.no = no
self.klass = klass
self.method = method
self.args = args
self.ret = ret
self.time = time
# Calculate hashvalue "cached" into a variable
self.hashvalue = hash(self.klass) ^ hash(self.method)
for mname, mobj in self.args:
self.hashvalue = self.hashvalue ^ hash(mname) ^ hash(mobj)
def visit(self, visitor):
visitor.visit_call(self)
def __hash__(self):
return self.hashvalue
def __eq__(self, other):
return self.hashvalue == other.hashvalue
class Trace:
def __init__(self, calls):
self.calls = calls
def visit(self, visitor):
visitor.visit_trace(self)
class Visitor:
def visit_literal(self, node):
raise NotImplementedError
def visit_blob(self, node):
raise NotImplementedError
def visit_named_constant(self, node):
raise NotImplementedError
def visit_array(self, node):
raise NotImplementedError
def visit_struct(self, node):
raise NotImplementedError
def visit_pointer(self, node):
raise NotImplementedError
def visit_call(self, node):
raise NotImplementedError
def visit_trace(self, node):
raise NotImplementedError
class PrettyPrinter:
def __init__(self, formatter, options):
self.formatter = formatter
self.options = options
def visit_literal(self, node):
if node.value is None:
self.formatter.literal('NULL')
return
if isinstance(node.value, str):
self.formatter.literal('"' + node.value + '"')
return
self.formatter.literal(repr(node.value))
def visit_blob(self, node):
self.formatter.address('blob()')
def visit_named_constant(self, node):
self.formatter.literal(node.name)
def visit_array(self, node):
self.formatter.text('{')
sep = ''
for value in node.elements:
self.formatter.text(sep)
value.visit(self)
sep = ', '
self.formatter.text('}')
def visit_struct(self, node):
self.formatter.text('{')
sep = ''
for name, value in node.members:
self.formatter.text(sep)
self.formatter.variable(name)
self.formatter.text(' = ')
value.visit(self)
sep = ', '
self.formatter.text('}')
def visit_pointer(self, node):
if self.options.named_ptrs:
self.formatter.address(node.named_address())
else:
self.formatter.address(node.address)
def visit_call(self, node):
if not self.options.suppress_variants:
self.formatter.text(f'{node.no} ')
if node.klass is not None:
self.formatter.function(node.klass + '::' + node.method)
else:
self.formatter.function(node.method)
if not self.options.method_only:
self.formatter.text('(')
sep = ''
for name, value in node.args:
self.formatter.text(sep)
self.formatter.variable(name)
self.formatter.text(' = ')
value.visit(self)
sep = ', '
self.formatter.text(')')
if node.ret is not None:
self.formatter.text(' = ')
node.ret.visit(self)
if not self.options.suppress_variants and node.time is not None:
self.formatter.text(' // time ')
node.time.visit(self)
self.formatter.newline()
def visit_trace(self, node):
for call in node.calls:
call.visit(self)