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