1#!/usr/bin/env python3 2########################################################################## 3# 4# Copyright 2008 VMware, Inc. 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 VMWARE 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'''Trace data model.''' 31 32 33import sys 34import string 35import binascii 36from io import StringIO 37import format 38 39 40class ModelOptions: 41 42 def __init__(self, args=None): 43 # Initialize the options we need to exist, 44 # with some reasonable defaults 45 self.plain = False 46 self.suppress_variants = False 47 self.named_ptrs = False 48 self.method_only = False 49 50 # If args is specified, we assume it is the result object 51 # from ArgumentParser.parse_args(). Copy the attribute values 52 # we have from it, if they exist. 53 if args is not None: 54 for var in self.__dict__: 55 if var in args.__dict__: 56 self.__dict__[var] = args.__dict__[var] 57 58 59class TraceStateData: 60 61 def __init__(self): 62 self.ptr_list = {} 63 self.ptr_type_list = {} 64 self.ptr_types_list = {} 65 66 67class Node: 68 69 def visit(self, visitor): 70 raise NotImplementedError 71 72 def __str__(self): 73 stream = StringIO() 74 formatter = format.Formatter(stream) 75 pretty_printer = PrettyPrinter(formatter, {}) 76 self.visit(pretty_printer) 77 return stream.getvalue() 78 79 def __hash__(self): 80 raise NotImplementedError 81 82 83class Literal(Node): 84 85 def __init__(self, value): 86 self.value = value 87 88 def visit(self, visitor): 89 visitor.visit_literal(self) 90 91 def __hash__(self): 92 return hash(self.value) 93 94 95class Blob(Node): 96 97 def __init__(self, value): 98 self.value = binascii.a2b_hex(value) 99 100 def getValue(self): 101 return self.value 102 103 def visit(self, visitor): 104 visitor.visit_blob(self) 105 106 def __hash__(self): 107 return hash(self.value) 108 109 110class NamedConstant(Node): 111 112 def __init__(self, name): 113 self.name = name 114 115 def visit(self, visitor): 116 visitor.visit_named_constant(self) 117 118 def __hash__(self): 119 return hash(self.name) 120 121 122class Array(Node): 123 124 def __init__(self, elements): 125 self.elements = elements 126 127 def visit(self, visitor): 128 visitor.visit_array(self) 129 130 def __hash__(self): 131 tmp = 0 132 for mobj in self.elements: 133 tmp = tmp ^ hash(mobj) 134 return tmp 135 136 137class Struct(Node): 138 139 def __init__(self, name, members): 140 self.name = name 141 self.members = members 142 143 def visit(self, visitor): 144 visitor.visit_struct(self) 145 146 def __hash__(self): 147 tmp = hash(self.name) 148 for mname, mobj in self.members: 149 tmp = tmp ^ hash(mname) ^ hash(mobj) 150 return tmp 151 152 153class Pointer(Node): 154 155 ptr_ignore_list = ["ret", "elem"] 156 157 def __init__(self, state, address, pname): 158 self.address = address 159 self.state = state 160 161 # Check if address exists in list and if it is a return value address 162 t1 = address in state.ptr_list 163 if t1: 164 rname = state.ptr_type_list[address] 165 t2 = rname in self.ptr_ignore_list and pname not in self.ptr_ignore_list 166 else: 167 rname = pname 168 t2 = False 169 170 # If address does NOT exist (add it), OR IS a ret value (update with new type) 171 if not t1 or t2: 172 # If previously set to ret value, remove one from count 173 if t1 and t2: 174 self.adjust_ptr_type_count(rname, -1) 175 176 # Add / update 177 self.adjust_ptr_type_count(pname, 1) 178 tmp = "{}_{}".format(pname, state.ptr_types_list[pname]) 179 state.ptr_list[address] = tmp 180 state.ptr_type_list[address] = pname 181 182 def adjust_ptr_type_count(self, pname, delta): 183 if pname not in self.state.ptr_types_list: 184 self.state.ptr_types_list[pname] = 0 185 186 self.state.ptr_types_list[pname] += delta 187 188 def named_address(self): 189 return self.state.ptr_list[self.address] 190 191 def visit(self, visitor): 192 visitor.visit_pointer(self) 193 194 def __hash__(self): 195 return hash(self.named_address()) 196 197 198class Call: 199 200 def __init__(self, no, klass, method, args, ret, time): 201 self.no = no 202 self.klass = klass 203 self.method = method 204 self.args = args 205 self.ret = ret 206 self.time = time 207 208 # Calculate hashvalue "cached" into a variable 209 self.hashvalue = hash(self.klass) ^ hash(self.method) 210 for mname, mobj in self.args: 211 self.hashvalue = self.hashvalue ^ hash(mname) ^ hash(mobj) 212 213 def visit(self, visitor): 214 visitor.visit_call(self) 215 216 def __hash__(self): 217 return self.hashvalue 218 219 def __eq__(self, other): 220 return self.hashvalue == other.hashvalue 221 222 223class Trace: 224 225 def __init__(self, calls): 226 self.calls = calls 227 228 def visit(self, visitor): 229 visitor.visit_trace(self) 230 231 232class Visitor: 233 234 def visit_literal(self, node): 235 raise NotImplementedError 236 237 def visit_blob(self, node): 238 raise NotImplementedError 239 240 def visit_named_constant(self, node): 241 raise NotImplementedError 242 243 def visit_array(self, node): 244 raise NotImplementedError 245 246 def visit_struct(self, node): 247 raise NotImplementedError 248 249 def visit_pointer(self, node): 250 raise NotImplementedError 251 252 def visit_call(self, node): 253 raise NotImplementedError 254 255 def visit_trace(self, node): 256 raise NotImplementedError 257 258 259class PrettyPrinter: 260 261 def __init__(self, formatter, options): 262 self.formatter = formatter 263 self.options = options 264 265 def visit_literal(self, node): 266 if node.value is None: 267 self.formatter.literal('NULL') 268 return 269 270 if isinstance(node.value, str): 271 self.formatter.literal('"' + node.value + '"') 272 return 273 274 self.formatter.literal(repr(node.value)) 275 276 def visit_blob(self, node): 277 self.formatter.address('blob()') 278 279 def visit_named_constant(self, node): 280 self.formatter.literal(node.name) 281 282 def visit_array(self, node): 283 self.formatter.text('{') 284 sep = '' 285 for value in node.elements: 286 self.formatter.text(sep) 287 value.visit(self) 288 sep = ', ' 289 self.formatter.text('}') 290 291 def visit_struct(self, node): 292 self.formatter.text('{') 293 sep = '' 294 for name, value in node.members: 295 self.formatter.text(sep) 296 self.formatter.variable(name) 297 self.formatter.text(' = ') 298 value.visit(self) 299 sep = ', ' 300 self.formatter.text('}') 301 302 def visit_pointer(self, node): 303 if self.options.named_ptrs: 304 self.formatter.address(node.named_address()) 305 else: 306 self.formatter.address(node.address) 307 308 def visit_call(self, node): 309 if not self.options.suppress_variants: 310 self.formatter.text(f'{node.no} ') 311 312 if node.klass is not None: 313 self.formatter.function(node.klass + '::' + node.method) 314 else: 315 self.formatter.function(node.method) 316 317 if not self.options.method_only: 318 self.formatter.text('(') 319 sep = '' 320 for name, value in node.args: 321 self.formatter.text(sep) 322 self.formatter.variable(name) 323 self.formatter.text(' = ') 324 value.visit(self) 325 sep = ', ' 326 self.formatter.text(')') 327 if node.ret is not None: 328 self.formatter.text(' = ') 329 node.ret.visit(self) 330 331 if not self.options.suppress_variants and node.time is not None: 332 self.formatter.text(' // time ') 333 node.time.visit(self) 334 335 self.formatter.newline() 336 337 def visit_trace(self, node): 338 for call in node.calls: 339 call.visit(self) 340 341