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