| #!/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) |
| |