Home | History | Annotate | Download | only in trace
      1 #!/usr/bin/env python
      2 ##########################################################################
      3 # 
      4 # Copyright 2008-2013, 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 import sys
     31 import struct
     32 import json
     33 import binascii
     34 import re
     35 import copy
     36 
     37 import model
     38 import parse as parser
     39 
     40 
     41 try:
     42     from struct import unpack_from
     43 except ImportError:
     44     def unpack_from(fmt, buf, offset=0):
     45         size = struct.calcsize(fmt)
     46         return struct.unpack(fmt, buf[offset:offset + size])
     47 
     48 #
     49 # Some constants
     50 #
     51 PIPE_BUFFER = 0
     52 PIPE_SHADER_VERTEX   = 0
     53 PIPE_SHADER_FRAGMENT = 1
     54 PIPE_SHADER_GEOMETRY = 2
     55 PIPE_SHADER_COMPUTE  = 3
     56 PIPE_SHADER_TYPES    = 4
     57 
     58 
     59 def serialize(obj):
     60     '''JSON serializer function for non-standard Python objects.'''
     61 
     62     if isinstance(obj, bytearray):
     63         # TODO: Decide on a single way of dumping blobs
     64         if False:
     65             # Don't dump full blobs, but merely a description of their size and
     66             # CRC32 hash.
     67             crc32 = binascii.crc32(obj)
     68             if crc32 < 0:
     69                 crc32 += 0x100000000
     70             return 'blob(size=%u,crc32=0x%08x)' % (len(obj), crc32)
     71         if True:
     72             # Dump blobs as an array of 16byte hexadecimals
     73             res = []
     74             for i in range(0, len(obj), 16):
     75                 res.append(binascii.b2a_hex(obj[i: i+16]))
     76             return res
     77         # Dump blobs as a single hexadecimal string
     78         return binascii.b2a_hex(obj)
     79 
     80     # If the object has a __json__ method, use it.
     81     try:
     82         method = obj.__json__
     83     except AttributeError:
     84         raise TypeError(obj)
     85     else:
     86         return method()
     87 
     88 
     89 class Struct:
     90     """C-like struct.
     91     
     92     Python doesn't have C structs, but do its dynamic nature, any object is
     93     pretty close.
     94     """
     95 
     96     def __json__(self):
     97         '''Convert the structure to a standard Python dict, so it can be
     98         serialized.'''
     99 
    100         obj = {}
    101         for name, value in self.__dict__.items():
    102             if not name.startswith('_'):
    103                 obj[name] = value
    104         return obj
    105 
    106     def __repr__(self):
    107         return repr(self.__json__())
    108 
    109 
    110 class Translator(model.Visitor):
    111     """Translate model arguments into regular Python objects"""
    112 
    113     def __init__(self, interpreter):
    114         self.interpreter = interpreter
    115         self.result = None
    116 
    117     def visit(self, node):
    118         self.result = None
    119         node.visit(self)
    120         return self.result
    121         
    122     def visit_literal(self, node):
    123         self.result = node.value
    124     
    125     def visit_blob(self, node):
    126         self.result = node
    127     
    128     def visit_named_constant(self, node):
    129         self.result = node.name
    130     
    131     def visit_array(self, node):
    132         array = []
    133         for element in node.elements:
    134             array.append(self.visit(element))
    135         self.result = array
    136     
    137     def visit_struct(self, node):
    138         struct = Struct()
    139         for member_name, member_node in node.members:
    140             member_value = self.visit(member_node)
    141             setattr(struct, member_name, member_value)
    142         self.result = struct
    143     
    144     def visit_pointer(self, node):
    145         self.result = self.interpreter.lookup_object(node.address)
    146 
    147 
    148 class Dispatcher:
    149     '''Base class for classes whose methods can dispatch Gallium calls.'''
    150     
    151     def __init__(self, interpreter):
    152         self.interpreter = interpreter
    153         
    154 
    155 class Global(Dispatcher):
    156     '''Global name space.
    157 
    158     For calls that are not associated with objects, i.e, functions and not
    159     methods.
    160     '''
    161 
    162     def pipe_screen_create(self):
    163         return Screen(self.interpreter)
    164     
    165     def pipe_context_create(self, screen):
    166         return screen.context_create()
    167 
    168     
    169 class Transfer:
    170     '''pipe_transfer'''
    171 
    172     def __init__(self, resource, usage, subresource, box):
    173         self.resource = resource
    174         self.usage = usage
    175         self.subresource = subresource
    176         self.box = box
    177 
    178 
    179 class Screen(Dispatcher):
    180     '''pipe_screen'''
    181     
    182     def __init__(self, interpreter):
    183         Dispatcher.__init__(self, interpreter)
    184 
    185     def destroy(self):
    186         pass
    187 
    188     def context_create(self):
    189         return Context(self.interpreter)
    190     
    191     def is_format_supported(self, format, target, sample_count, bind, geom_flags):
    192         pass
    193     
    194     def resource_create(self, templat):
    195         resource = templat
    196         # Normalize state to avoid spurious differences
    197         if resource.nr_samples == 0:
    198             resource.nr_samples = 1
    199         if resource.target == PIPE_BUFFER:
    200             # We will keep track of buffer contents
    201             resource.data = bytearray(resource.width)
    202             # Ignore format
    203             del resource.format
    204         return resource
    205 
    206     def resource_destroy(self, resource):
    207         self.interpreter.unregister_object(resource)
    208 
    209     def fence_finish(self, fence, timeout=None):
    210         pass
    211     
    212     def fence_signalled(self, fence):
    213         pass
    214     
    215     def fence_reference(self, dst, src):
    216         pass
    217     
    218     def flush_frontbuffer(self, resource):
    219         pass
    220 
    221 
    222 class Context(Dispatcher):
    223     '''pipe_context'''
    224 
    225     # Internal methods variable should be prefixed with '_'
    226     
    227     def __init__(self, interpreter):
    228         Dispatcher.__init__(self, interpreter)
    229 
    230         # Setup initial state
    231         self._state = Struct()
    232         self._state.scissors = []
    233         self._state.viewports = []
    234         self._state.vertex_buffers = []
    235         self._state.vertex_elements = []
    236         self._state.vs = Struct()
    237         self._state.gs = Struct()
    238         self._state.fs = Struct()
    239         self._state.vs.shader = None
    240         self._state.gs.shader = None
    241         self._state.fs.shader = None
    242         self._state.vs.sampler = []
    243         self._state.gs.sampler = []
    244         self._state.fs.sampler = []
    245         self._state.vs.sampler_views = []
    246         self._state.gs.sampler_views = []
    247         self._state.fs.sampler_views = []
    248         self._state.vs.constant_buffer = []
    249         self._state.gs.constant_buffer = []
    250         self._state.fs.constant_buffer = []
    251         self._state.render_condition_condition = 0
    252         self._state.render_condition_mode = 0
    253 
    254         self._draw_no = 0
    255 
    256     def destroy(self):
    257         pass
    258     
    259     def create_blend_state(self, state):
    260         # Normalize state to avoid spurious differences
    261         if not state.logicop_enable:
    262             del state.logicop_func
    263         if not state.rt[0].blend_enable:
    264             del state.rt[0].rgb_src_factor
    265             del state.rt[0].rgb_dst_factor
    266             del state.rt[0].rgb_func
    267             del state.rt[0].alpha_src_factor
    268             del state.rt[0].alpha_dst_factor
    269             del state.rt[0].alpha_func
    270         return state
    271 
    272     def bind_blend_state(self, state):
    273         # Normalize state
    274         self._state.blend = state
    275 
    276     def delete_blend_state(self, state):
    277         pass
    278     
    279     def create_sampler_state(self, state):
    280         return state
    281 
    282     def delete_sampler_state(self, state):
    283         pass
    284 
    285     def bind_sampler_states(self, shader, start, num_states, states):
    286         # FIXME: Handle non-zero start
    287         assert start == 0
    288         self._get_stage_state(shader).sampler = states
    289 
    290     def bind_vertex_sampler_states(self, num_states, states):
    291         # XXX: deprecated method
    292         self._state.vs.sampler = states
    293 
    294     def bind_geometry_sampler_states(self, num_states, states):
    295         # XXX: deprecated method
    296         self._state.gs.sampler = states
    297 
    298     def bind_fragment_sampler_states(self, num_states, states):
    299         # XXX: deprecated method
    300         self._state.fs.sampler = states
    301         
    302     def create_rasterizer_state(self, state):
    303         return state
    304 
    305     def bind_rasterizer_state(self, state):
    306         self._state.rasterizer = state
    307         
    308     def delete_rasterizer_state(self, state):
    309         pass
    310     
    311     def create_depth_stencil_alpha_state(self, state):
    312         # Normalize state to avoid spurious differences
    313         if not state.alpha.enabled:
    314             del state.alpha.func
    315             del state.alpha.ref_value
    316         for i in range(2):
    317             if not state.stencil[i].enabled:
    318                 del state.stencil[i].func
    319         return state
    320 
    321     def bind_depth_stencil_alpha_state(self, state):
    322         self._state.depth_stencil_alpha = state
    323             
    324     def delete_depth_stencil_alpha_state(self, state):
    325         pass
    326 
    327     _tokenLabelRE = re.compile('^\s*\d+: ', re.MULTILINE)
    328 
    329     def _create_shader_state(self, state):
    330         # Strip the labels from the tokens
    331         if state.tokens is not None:
    332             state.tokens = self._tokenLabelRE.sub('', state.tokens)
    333         return state
    334 
    335     create_vs_state = _create_shader_state
    336     create_gs_state = _create_shader_state
    337     create_fs_state = _create_shader_state
    338     
    339     def bind_vs_state(self, state):
    340         self._state.vs.shader = state
    341 
    342     def bind_gs_state(self, state):
    343         self._state.gs.shader = state
    344         
    345     def bind_fs_state(self, state):
    346         self._state.fs.shader = state
    347         
    348     def _delete_shader_state(self, state):
    349         return state
    350 
    351     delete_vs_state = _delete_shader_state
    352     delete_gs_state = _delete_shader_state
    353     delete_fs_state = _delete_shader_state
    354 
    355     def set_blend_color(self, state):
    356         self._state.blend_color = state
    357 
    358     def set_stencil_ref(self, state):
    359         self._state.stencil_ref = state
    360 
    361     def set_clip_state(self, state):
    362         self._state.clip = state
    363 
    364     def _dump_constant_buffer(self, buffer):
    365         if not self.interpreter.verbosity(2):
    366             return
    367 
    368         data = self.real.buffer_read(buffer)
    369         format = '4f'
    370         index = 0
    371         for offset in range(0, len(data), struct.calcsize(format)):
    372             x, y, z, w = unpack_from(format, data, offset)
    373             sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))
    374             index += 1
    375         sys.stdout.flush()
    376 
    377     def _get_stage_state(self, shader):
    378         if shader == PIPE_SHADER_VERTEX:
    379             return self._state.vs
    380         if shader == PIPE_SHADER_GEOMETRY:
    381             return self._state.gs
    382         if shader == PIPE_SHADER_FRAGMENT:
    383             return self._state.fs
    384         assert False
    385 
    386     def set_constant_buffer(self, shader, index, constant_buffer):
    387         self._update(self._get_stage_state(shader).constant_buffer, index, 1, [constant_buffer])
    388 
    389     def set_framebuffer_state(self, state):
    390         self._state.fb = state
    391 
    392     def set_polygon_stipple(self, state):
    393         self._state.polygon_stipple = state
    394 
    395     def _update(self, array, start_slot, num_slots, states):
    396         if not isinstance(states, list):
    397             # XXX: trace is not serializing multiple scissors/viewports properly yet
    398             num_slots = 1
    399             states = [states]
    400         while len(array) < start_slot + num_slots:
    401             array.append(None)
    402         for i in range(num_slots):
    403             array[start_slot + i] = states[i]
    404 
    405     def set_scissor_states(self, start_slot, num_scissors, states):
    406         self._update(self._state.scissors, start_slot, num_scissors, states)
    407 
    408     def set_viewport_states(self, start_slot, num_viewports, states):
    409         self._update(self._state.viewports, start_slot, num_viewports, states)
    410 
    411     def create_sampler_view(self, resource, templ):
    412         templ.resource = resource
    413         return templ
    414 
    415     def sampler_view_destroy(self, view):
    416         pass
    417 
    418     def set_sampler_views(self, shader, start, num, views):
    419         # FIXME: Handle non-zero start
    420         assert start == 0
    421         self._get_stage_state(shader).sampler_views = views
    422 
    423     def set_fragment_sampler_views(self, num, views):
    424         # XXX: deprecated
    425         self._state.fs.sampler_views = views
    426 
    427     def set_geometry_sampler_views(self, num, views):
    428         # XXX: deprecated
    429         self._state.gs.sampler_views = views
    430 
    431     def set_vertex_sampler_views(self, num, views):
    432         # XXX: deprecated
    433         self._state.vs.sampler_views = views
    434 
    435     def set_vertex_buffers(self, start_slot, num_buffers, buffers):
    436         self._update(self._state.vertex_buffers, start_slot, num_buffers, buffers)
    437             
    438     def create_vertex_elements_state(self, num_elements, elements):
    439         return elements[0:num_elements]
    440 
    441     def bind_vertex_elements_state(self, state):
    442         self._state.vertex_elements = state
    443 
    444     def delete_vertex_elements_state(self, state):
    445         pass
    446 
    447     def set_index_buffer(self, ib):
    448         self._state.index_buffer = ib
    449 
    450     # Don't dump more than this number of indices/vertices
    451     MAX_ELEMENTS = 16
    452 
    453     def _merge_indices(self, info):
    454         '''Merge the vertices into our state.'''
    455 
    456         index_size = self._state.index_buffer.index_size
    457         
    458         format = {
    459             1: 'B',
    460             2: 'H',
    461             4: 'I',
    462         }[index_size]
    463 
    464         assert struct.calcsize(format) == index_size
    465 
    466         if self._state.index_buffer.buffer is None:
    467             # Could happen with index in user memory
    468             return 0, 0
    469 
    470         data = self._state.index_buffer.buffer.data
    471         max_index, min_index = 0, 0xffffffff
    472 
    473         count = min(info.count, self.MAX_ELEMENTS)
    474         indices = []
    475         for i in xrange(info.start, info.start + count):
    476             offset = self._state.index_buffer.offset + i*index_size
    477             if offset + index_size > len(data):
    478                 index = 0
    479             else:
    480                 index, = unpack_from(format, data, offset)
    481             indices.append(index)
    482             min_index = min(min_index, index)
    483             max_index = max(max_index, index)
    484 
    485         self._state.indices = indices
    486 
    487         return min_index + info.index_bias, max_index + info.index_bias
    488 
    489     def _merge_vertices(self, start, count):
    490         '''Merge the vertices into our state.'''
    491 
    492         count = min(count, self.MAX_ELEMENTS)
    493         vertices = []
    494         for index in xrange(start, start + count):
    495             if index >= start + 16:
    496                 sys.stdout.write('\t...\n')
    497                 break
    498             vertex = []
    499             for velem in self._state.vertex_elements:
    500                 vbuf = self._state.vertex_buffers[velem.vertex_buffer_index]
    501                 if vbuf.buffer is None:
    502                     continue
    503 
    504                 data = vbuf.buffer.data
    505 
    506                 offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index
    507                 format = {
    508                     'PIPE_FORMAT_R32_FLOAT': 'f',
    509                     'PIPE_FORMAT_R32G32_FLOAT': '2f',
    510                     'PIPE_FORMAT_R32G32B32_FLOAT': '3f',
    511                     'PIPE_FORMAT_R32G32B32A32_FLOAT': '4f',
    512                     'PIPE_FORMAT_R32_UINT': 'I',
    513                     'PIPE_FORMAT_R32G32_UINT': '2I',
    514                     'PIPE_FORMAT_R32G32B32_UINT': '3I',
    515                     'PIPE_FORMAT_R32G32B32A32_UINT': '4I',
    516                     'PIPE_FORMAT_R8_UINT': 'B',
    517                     'PIPE_FORMAT_R8G8_UINT': '2B',
    518                     'PIPE_FORMAT_R8G8B8_UINT': '3B',
    519                     'PIPE_FORMAT_R8G8B8A8_UINT': '4B',
    520                     'PIPE_FORMAT_A8R8G8B8_UNORM': '4B',
    521                     'PIPE_FORMAT_R8G8B8A8_UNORM': '4B',
    522                     'PIPE_FORMAT_B8G8R8A8_UNORM': '4B',
    523                     'PIPE_FORMAT_R16G16B16_SNORM': '3h',
    524                 }[velem.src_format]
    525 
    526                 data = vbuf.buffer.data
    527                 attribute = unpack_from(format, data, offset)
    528                 vertex.append(attribute)
    529 
    530             vertices.append(vertex)
    531 
    532         self._state.vertices = vertices
    533 
    534     def render_condition(self, query, condition = 0, mode = 0):
    535         self._state.render_condition_query = query
    536         self._state.render_condition_condition = condition
    537         self._state.render_condition_mode = mode
    538 
    539     def set_stream_output_targets(self, num_targets, tgs, offsets):
    540         self._state.so_targets = tgs
    541         self._state.offsets = offsets
    542 
    543     def draw_vbo(self, info):
    544         self._draw_no += 1
    545 
    546         if self.interpreter.call_no < self.interpreter.options.call and \
    547             self._draw_no < self.interpreter.options.draw:
    548                 return
    549 
    550         # Merge the all draw state
    551 
    552         self._state.draw = info
    553 
    554         if info.indexed:
    555             min_index, max_index = self._merge_indices(info)
    556         else:
    557             min_index = info.start
    558             max_index = info.start + info.count - 1
    559         self._merge_vertices(min_index, max_index - min_index + 1)
    560 
    561         self._dump_state()
    562 
    563     _dclRE = re.compile('^DCL\s+(IN|OUT|SAMP|SVIEW)\[([0-9]+)\].*$', re.MULTILINE)
    564 
    565     def _normalize_stage_state(self, stage):
    566 
    567         registers = {}
    568 
    569         if stage.shader is not None and stage.shader.tokens is not None:
    570             for mo in self._dclRE.finditer(stage.shader.tokens):
    571                 file_ = mo.group(1)
    572                 index = mo.group(2)
    573                 register = registers.setdefault(file_, set())
    574                 register.add(int(index))
    575 
    576         if 'SAMP' in registers and 'SVIEW' not in registers:
    577             registers['SVIEW'] = registers['SAMP']
    578 
    579         mapping = [
    580             #("CONST", "constant_buffer"),
    581             ("SAMP", "sampler"),
    582             ("SVIEW", "sampler_views"),
    583         ]
    584 
    585         for fileName, attrName in mapping:
    586             register = registers.setdefault(fileName, set())
    587             attr = getattr(stage, attrName)
    588             for index in range(len(attr)):
    589                 if index not in register:
    590                     attr[index] = None
    591             while attr and attr[-1] is None:
    592                 attr.pop()
    593 
    594     def _dump_state(self):
    595         '''Dump our state to JSON and terminate.'''
    596 
    597         state = copy.deepcopy(self._state)
    598 
    599         self._normalize_stage_state(state.vs)
    600         self._normalize_stage_state(state.gs)
    601         self._normalize_stage_state(state.fs)
    602 
    603         json.dump(
    604             obj = state,
    605             fp = sys.stdout,
    606             default = serialize,
    607             sort_keys = True,
    608             indent = 4,
    609             separators = (',', ': ')
    610         )
    611 
    612         sys.exit(0)
    613 
    614     def resource_copy_region(self, dst, dst_level, dstx, dsty, dstz, src, src_level, src_box):
    615         if dst.target == PIPE_BUFFER or src.target == PIPE_BUFFER:
    616             assert dst.target == PIPE_BUFFER and src.target == PIPE_BUFFER
    617             assert dst_level == 0
    618             assert dsty == 0
    619             assert dstz == 0
    620             assert src_level == 0
    621             assert src_box.y == 0
    622             assert src_box.z == 0
    623             assert src_box.height == 1
    624             assert src_box.depth == 1
    625             dst.data[dstx : dstx + src_box.width] = src.data[src_box.x : src_box.x + src_box.width]
    626         pass
    627 
    628     def is_resource_referenced(self, texture, face, level):
    629         pass
    630     
    631     def get_transfer(self, texture, sr, usage, box):
    632         if texture is None:
    633             return None
    634         transfer = Transfer(texture, sr, usage, box)
    635         return transfer
    636     
    637     def tex_transfer_destroy(self, transfer):
    638         self.interpreter.unregister_object(transfer)
    639 
    640     def transfer_inline_write(self, resource, level, usage, box, stride, layer_stride, data):
    641         if resource is not None and resource.target == PIPE_BUFFER:
    642             data = data.getValue()
    643             assert len(data) >= box.width
    644             assert box.x + box.width <= len(resource.data)
    645             resource.data[box.x : box.x + box.width] = data[:box.width]
    646 
    647     def flush(self, flags):
    648         # Return a fake fence
    649         return self.interpreter.call_no
    650 
    651     def clear(self, buffers, color, depth, stencil):
    652         pass
    653         
    654     def clear_render_target(self, dst, rgba, dstx, dsty, width, height):
    655         pass
    656 
    657     def clear_depth_stencil(self, dst, clear_flags, depth, stencil, dstx, dsty, width, height):
    658         pass
    659 
    660     def create_surface(self, resource, surf_tmpl):
    661         assert resource is not None
    662         surf_tmpl.resource = resource
    663         return surf_tmpl
    664 
    665     def surface_destroy(self, surface):
    666         self.interpreter.unregister_object(surface)
    667 
    668     def create_query(self, query_type, index):
    669         return query_type
    670     
    671     def destroy_query(self, query):
    672         pass
    673 
    674     def begin_query(self, query):
    675         pass
    676 
    677     def end_query(self, query):
    678         pass
    679 
    680     def create_stream_output_target(self, res, buffer_offset, buffer_size):
    681         so_target = Struct()
    682         so_target.resource = res
    683         so_target.offset = buffer_offset
    684         so_target.size = buffer_size
    685         return so_target
    686 
    687 
    688 class Interpreter(parser.TraceDumper):
    689     '''Specialization of a trace parser that interprets the calls as it goes
    690     along.'''
    691     
    692     ignoredCalls = set((
    693             ('pipe_screen', 'is_format_supported'),
    694             ('pipe_screen', 'get_name'),
    695             ('pipe_screen', 'get_vendor'),
    696             ('pipe_screen', 'get_param'),
    697             ('pipe_screen', 'get_paramf'),
    698             ('pipe_screen', 'get_shader_param'),
    699             ('pipe_context', 'clear_render_target'), # XXX workaround trace bugs
    700     ))
    701 
    702     def __init__(self, stream, options):
    703         parser.TraceDumper.__init__(self, stream, sys.stderr)
    704         self.options = options
    705         self.objects = {}
    706         self.result = None
    707         self.globl = Global(self)
    708         self.call_no = None
    709 
    710     def register_object(self, address, object):
    711         self.objects[address] = object
    712         
    713     def unregister_object(self, object):
    714         # TODO
    715         pass
    716 
    717     def lookup_object(self, address):
    718         try:
    719             return self.objects[address]
    720         except KeyError:
    721             # Could happen, e.g., with user memory pointers
    722             return address
    723     
    724     def interpret(self, trace):
    725         for call in trace.calls:
    726             self.interpret_call(call)
    727 
    728     def handle_call(self, call):
    729         if (call.klass, call.method) in self.ignoredCalls:
    730             return
    731 
    732         self.call_no = call.no
    733 
    734         if self.verbosity(1):
    735             # Write the call to stderr (as stdout would corrupt the JSON output)
    736             sys.stderr.flush()
    737             sys.stdout.flush()
    738             parser.TraceDumper.handle_call(self, call)
    739             sys.stderr.flush()
    740             sys.stdout.flush()
    741         
    742         args = [(str(name), self.interpret_arg(arg)) for name, arg in call.args] 
    743         
    744         if call.klass:
    745             name, obj = args[0]
    746             args = args[1:]
    747         else:
    748             obj = self.globl
    749             
    750         method = getattr(obj, call.method)
    751         ret = method(**dict(args))
    752         
    753         # Keep track of created pointer objects.
    754         if call.ret and isinstance(call.ret, model.Pointer):
    755             if ret is None:
    756                 sys.stderr.write('warning: NULL returned\n')
    757             self.register_object(call.ret.address, ret)
    758 
    759         self.call_no = None
    760 
    761     def interpret_arg(self, node):
    762         translator = Translator(self)
    763         return translator.visit(node)
    764 
    765     def verbosity(self, level):
    766         return self.options.verbosity >= level
    767     
    768 
    769 class Main(parser.Main):
    770 
    771     def get_optparser(self):
    772         '''Custom options.'''
    773 
    774         optparser = parser.Main.get_optparser(self)
    775         optparser.add_option("-q", "--quiet", action="store_const", const=0, dest="verbosity", help="no messages")
    776         optparser.add_option("-v", "--verbose", action="count", dest="verbosity", default=0, help="increase verbosity level")
    777         optparser.add_option("-c", "--call", action="store", type="int", dest="call", default=0xffffffff, help="dump on this call")
    778         optparser.add_option("-d", "--draw", action="store", type="int", dest="draw", default=0xffffffff, help="dump on this draw")
    779         return optparser
    780 
    781     def process_arg(self, stream, options):
    782         parser = Interpreter(stream, options)
    783         parser.parse()
    784 
    785 
    786 if __name__ == '__main__':
    787     Main().main()
    788