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