1/* 2 * Copyright (C) 2017-2019 Alyssa Rosenzweig 3 * Copyright (C) 2017-2019 Connor Abbott 4 * Copyright (C) 2019 Collabora, Ltd. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 */ 25 26#include <agx_pack.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <memory.h> 30#include <stdbool.h> 31#include <stdarg.h> 32#include <ctype.h> 33#include <sys/mman.h> 34 35#include "decode.h" 36#include "io.h" 37#include "hexdump.h" 38 39static const char *agx_alloc_types[AGX_NUM_ALLOC] = { "mem", "map", "cmd" }; 40 41static void 42agx_disassemble(void *_code, size_t maxlen, FILE *fp) 43{ 44 /* stub */ 45} 46 47FILE *agxdecode_dump_stream; 48 49#define MAX_MAPPINGS 4096 50 51struct agx_bo mmap_array[MAX_MAPPINGS]; 52unsigned mmap_count = 0; 53 54struct agx_bo *ro_mappings[MAX_MAPPINGS]; 55unsigned ro_mapping_count = 0; 56 57static struct agx_bo * 58agxdecode_find_mapped_gpu_mem_containing_rw(uint64_t addr) 59{ 60 for (unsigned i = 0; i < mmap_count; ++i) { 61 if (mmap_array[i].type == AGX_ALLOC_REGULAR && addr >= mmap_array[i].ptr.gpu && (addr - mmap_array[i].ptr.gpu) < mmap_array[i].size) 62 return mmap_array + i; 63 } 64 65 return NULL; 66} 67 68static struct agx_bo * 69agxdecode_find_mapped_gpu_mem_containing(uint64_t addr) 70{ 71 struct agx_bo *mem = agxdecode_find_mapped_gpu_mem_containing_rw(addr); 72 73 if (mem && mem->ptr.cpu && !mem->ro) { 74 mprotect(mem->ptr.cpu, mem->size, PROT_READ); 75 mem->ro = true; 76 ro_mappings[ro_mapping_count++] = mem; 77 assert(ro_mapping_count < MAX_MAPPINGS); 78 } 79 80 if (mem && !mem->mapped) { 81 fprintf(stderr, "[ERROR] access to memory not mapped (GPU %" PRIx64 ", handle %u)\n", mem->ptr.gpu, mem->handle); 82 } 83 84 return mem; 85} 86 87static struct agx_bo * 88agxdecode_find_handle(unsigned handle, unsigned type) 89{ 90 for (unsigned i = 0; i < mmap_count; ++i) { 91 if (mmap_array[i].type != type) 92 continue; 93 94 if (mmap_array[i].handle != handle) 95 continue; 96 97 return &mmap_array[i]; 98 } 99 100 return NULL; 101} 102 103static void 104agxdecode_mark_mapped(unsigned handle) 105{ 106 struct agx_bo *bo = agxdecode_find_handle(handle, AGX_ALLOC_REGULAR); 107 108 if (!bo) { 109 fprintf(stderr, "ERROR - unknown BO mapped with handle %u\n", handle); 110 return; 111 } 112 113 /* Mark mapped for future consumption */ 114 bo->mapped = true; 115} 116 117static void 118agxdecode_decode_segment_list(void *segment_list) 119{ 120 unsigned nr_handles = 0; 121 122 /* First, mark everything unmapped */ 123 for (unsigned i = 0; i < mmap_count; ++i) 124 mmap_array[i].mapped = false; 125 126 /* Check the header */ 127 struct agx_map_header *hdr = segment_list; 128 if (hdr->resource_group_count == 0) { 129 fprintf(agxdecode_dump_stream, "ERROR - empty map\n"); 130 return; 131 } 132 133 if (hdr->segment_count != 1) { 134 fprintf(agxdecode_dump_stream, "ERROR - can't handle segment count %u\n", 135 hdr->segment_count); 136 } 137 138 fprintf(agxdecode_dump_stream, "Segment list:\n"); 139 fprintf(agxdecode_dump_stream, " Command buffer shmem ID: %" PRIx64 "\n", hdr->cmdbuf_id); 140 fprintf(agxdecode_dump_stream, " Encoder ID: %" PRIx64 "\n", hdr->encoder_id); 141 fprintf(agxdecode_dump_stream, " Kernel commands start offset: %u\n", 142 hdr->kernel_commands_start_offset); 143 fprintf(agxdecode_dump_stream, " Kernel commands end offset: %u\n", 144 hdr->kernel_commands_end_offset); 145 fprintf(agxdecode_dump_stream, " Unknown: 0x%X\n", hdr->unk); 146 147 /* Expected structure: header followed by resource groups */ 148 size_t length = sizeof(struct agx_map_header); 149 length += sizeof(struct agx_map_entry) * hdr->resource_group_count; 150 151 if (length != hdr->length) { 152 fprintf(agxdecode_dump_stream, "ERROR: expected length %zu, got %u\n", 153 length, hdr->length); 154 } 155 156 if (hdr->padding[0] || hdr->padding[1]) 157 fprintf(agxdecode_dump_stream, "ERROR - padding tripped\n"); 158 159 /* Check the entries */ 160 struct agx_map_entry *groups = ((void *) hdr) + sizeof(*hdr); 161 for (unsigned i = 0; i < hdr->resource_group_count; ++i) { 162 struct agx_map_entry group = groups[i]; 163 unsigned count = group.resource_count; 164 165 STATIC_ASSERT(ARRAY_SIZE(group.resource_id) == 6); 166 STATIC_ASSERT(ARRAY_SIZE(group.resource_unk) == 6); 167 STATIC_ASSERT(ARRAY_SIZE(group.resource_flags) == 6); 168 169 if ((count < 1) || (count > 6)) { 170 fprintf(agxdecode_dump_stream, "ERROR - invalid count %u\n", count); 171 continue; 172 } 173 174 for (unsigned j = 0; j < count; ++j) { 175 unsigned handle = group.resource_id[j]; 176 unsigned unk = group.resource_unk[j]; 177 unsigned flags = group.resource_flags[j]; 178 179 if (!handle) { 180 fprintf(agxdecode_dump_stream, "ERROR - invalid handle %u\n", handle); 181 continue; 182 } 183 184 agxdecode_mark_mapped(handle); 185 nr_handles++; 186 187 fprintf(agxdecode_dump_stream, "%u (0x%X, 0x%X)\n", handle, unk, flags); 188 } 189 190 if (group.unka) 191 fprintf(agxdecode_dump_stream, "ERROR - unknown 0x%X\n", group.unka); 192 193 /* Visual separator for resource groups */ 194 fprintf(agxdecode_dump_stream, "\n"); 195 } 196 197 /* Check the handle count */ 198 if (nr_handles != hdr->total_resources) { 199 fprintf(agxdecode_dump_stream, "ERROR - wrong handle count, got %u, expected %u (%u entries)\n", 200 nr_handles, hdr->total_resources, hdr->resource_group_count); 201 } 202} 203 204static inline void * 205__agxdecode_fetch_gpu_mem(const struct agx_bo *mem, 206 uint64_t gpu_va, size_t size, 207 int line, const char *filename) 208{ 209 if (!mem) 210 mem = agxdecode_find_mapped_gpu_mem_containing(gpu_va); 211 212 if (!mem) { 213 fprintf(stderr, "Access to unknown memory %" PRIx64 " in %s:%d\n", 214 gpu_va, filename, line); 215 fflush(agxdecode_dump_stream); 216 assert(0); 217 } 218 219 assert(mem); 220 assert(size + (gpu_va - mem->ptr.gpu) <= mem->size); 221 222 return mem->ptr.cpu + gpu_va - mem->ptr.gpu; 223} 224 225#define agxdecode_fetch_gpu_mem(gpu_va, size) \ 226 __agxdecode_fetch_gpu_mem(NULL, gpu_va, size, __LINE__, __FILE__) 227 228static void 229agxdecode_map_read_write(void) 230{ 231 for (unsigned i = 0; i < ro_mapping_count; ++i) { 232 ro_mappings[i]->ro = false; 233 mprotect(ro_mappings[i]->ptr.cpu, ro_mappings[i]->size, 234 PROT_READ | PROT_WRITE); 235 } 236 237 ro_mapping_count = 0; 238} 239 240/* Helpers for parsing the cmdstream */ 241 242#define DUMP_UNPACKED(T, var, str) { \ 243 agxdecode_log(str); \ 244 agx_print(agxdecode_dump_stream, T, var, (agxdecode_indent + 1) * 2); \ 245} 246 247#define DUMP_CL(T, cl, str) {\ 248 agx_unpack(agxdecode_dump_stream, cl, T, temp); \ 249 DUMP_UNPACKED(T, temp, str "\n"); \ 250} 251 252#define agxdecode_log(str) fputs(str, agxdecode_dump_stream) 253#define agxdecode_msg(str) fprintf(agxdecode_dump_stream, "// %s", str) 254 255unsigned agxdecode_indent = 0; 256uint64_t pipeline_base = 0; 257 258static void 259agxdecode_dump_bo(struct agx_bo *bo, const char *name) 260{ 261 fprintf(agxdecode_dump_stream, "%s %s (%u)\n", name, bo->name ?: "", bo->handle); 262 hexdump(agxdecode_dump_stream, bo->ptr.cpu, bo->size, false); 263} 264 265/* Abstraction for command stream parsing */ 266typedef unsigned (*decode_cmd)(const uint8_t *map, bool verbose); 267 268#define STATE_DONE (0xFFFFFFFFu) 269 270static void 271agxdecode_stateful(uint64_t va, const char *label, decode_cmd decoder, bool verbose) 272{ 273 struct agx_bo *alloc = agxdecode_find_mapped_gpu_mem_containing(va); 274 assert(alloc != NULL && "nonexistant object"); 275 fprintf(agxdecode_dump_stream, "%s (%" PRIx64 ", handle %u)\n", label, va, alloc->handle); 276 fflush(agxdecode_dump_stream); 277 278 uint8_t *map = agxdecode_fetch_gpu_mem(va, 64); 279 uint8_t *end = (uint8_t *) alloc->ptr.cpu + alloc->size; 280 281 if (verbose) 282 agxdecode_dump_bo(alloc, label); 283 fflush(agxdecode_dump_stream); 284 285 while (map < end) { 286 unsigned count = decoder(map, verbose); 287 288 /* If we fail to decode, default to a hexdump (don't hang) */ 289 if (count == 0) { 290 hexdump(agxdecode_dump_stream, map, 8, false); 291 count = 8; 292 } 293 294 map += count; 295 fflush(agxdecode_dump_stream); 296 297 if (count == STATE_DONE) 298 break; 299 } 300} 301 302unsigned COUNTER = 0; 303static unsigned 304agxdecode_pipeline(const uint8_t *map, UNUSED bool verbose) 305{ 306 uint8_t zeroes[16] = { 0 }; 307 308 if (map[0] == 0x4D && map[1] == 0xbd) { 309 /* TODO: Disambiguation for extended is a guess */ 310 agx_unpack(agxdecode_dump_stream, map, SET_SHADER_EXTENDED, cmd); 311 DUMP_UNPACKED(SET_SHADER_EXTENDED, cmd, "Set shader\n"); 312 313 if (cmd.preshader_mode == AGX_PRESHADER_MODE_PRESHADER) { 314 agxdecode_log("Preshader\n"); 315 agx_disassemble(agxdecode_fetch_gpu_mem(cmd.preshader_code, 2048), 316 2048, agxdecode_dump_stream); 317 agxdecode_log("\n---\n"); 318 } 319 320 agxdecode_log("\n"); 321 agx_disassemble(agxdecode_fetch_gpu_mem(cmd.code, 2048), 322 2048, agxdecode_dump_stream); 323 agxdecode_log("\n"); 324 325 char *name; 326 asprintf(&name, "file%u.bin", COUNTER++); 327 FILE *fp = fopen(name, "wb"); 328 fwrite(agxdecode_fetch_gpu_mem(cmd.code, 2048), 1, 2048, fp); 329 fclose(fp); 330 free(name); 331 agxdecode_log("\n"); 332 333 return AGX_SET_SHADER_EXTENDED_LENGTH; 334 } else if (map[0] == 0x4D) { 335 agx_unpack(agxdecode_dump_stream, map, SET_SHADER, cmd); 336 DUMP_UNPACKED(SET_SHADER, cmd, "Set shader\n"); 337 fflush(agxdecode_dump_stream); 338 339 if (cmd.preshader_mode == AGX_PRESHADER_MODE_PRESHADER) { 340 agxdecode_log("Preshader\n"); 341 agx_disassemble(agxdecode_fetch_gpu_mem(cmd.preshader_code, 2048), 342 2048, agxdecode_dump_stream); 343 agxdecode_log("\n---\n"); 344 } 345 346 agxdecode_log("\n"); 347 agx_disassemble(agxdecode_fetch_gpu_mem(cmd.code, 2048), 348 2048, agxdecode_dump_stream); 349 char *name; 350 asprintf(&name, "file%u.bin", COUNTER++); 351 FILE *fp = fopen(name, "wb"); 352 fwrite(agxdecode_fetch_gpu_mem(cmd.code, 2048), 1, 2048, fp); 353 fclose(fp); 354 free(name); 355 agxdecode_log("\n"); 356 357 return AGX_SET_SHADER_LENGTH; 358 } else if (map[0] == 0xDD) { 359 agx_unpack(agxdecode_dump_stream, map, BIND_TEXTURE, temp); 360 DUMP_UNPACKED(BIND_TEXTURE, temp, "Bind texture\n"); 361 362 uint8_t *tex = agxdecode_fetch_gpu_mem(temp.buffer, 64); 363 /* Texture length seen to be <= 0x18 bytes, samplers only need 8 byte 364 * alignment */ 365 agx_unpack(agxdecode_dump_stream, tex, TEXTURE, t); 366 DUMP_CL(TEXTURE, tex, "Texture"); 367 DUMP_CL(RENDER_TARGET, tex, "Render target"); 368 369 return AGX_BIND_TEXTURE_LENGTH; 370 } else if (map[0] == 0x9D) { 371 agx_unpack(agxdecode_dump_stream, map, BIND_SAMPLER, temp); 372 DUMP_UNPACKED(BIND_SAMPLER, temp, "Bind sampler\n"); 373 374 uint8_t *samp = agxdecode_fetch_gpu_mem(temp.buffer, 64); 375 DUMP_CL(SAMPLER, samp, "Sampler"); 376 hexdump(agxdecode_dump_stream, samp + AGX_SAMPLER_LENGTH, 64 - AGX_SAMPLER_LENGTH, false); 377 378 return AGX_BIND_SAMPLER_LENGTH; 379 } else if (map[0] == 0x1D) { 380 DUMP_CL(BIND_UNIFORM, map, "Bind uniform"); 381 return AGX_BIND_UNIFORM_LENGTH; 382 } else if (memcmp(map, zeroes, 16) == 0) { 383 /* TODO: Termination */ 384 return STATE_DONE; 385 } else { 386 return 0; 387 } 388} 389 390static void 391agxdecode_record(uint64_t va, size_t size, bool verbose) 392{ 393 uint8_t *map = agxdecode_fetch_gpu_mem(va, size); 394 uint32_t tag = 0; 395 memcpy(&tag, map, 4); 396 397 if (tag == 0x00000C00) { 398 assert(size == AGX_VIEWPORT_LENGTH); 399 DUMP_CL(VIEWPORT, map, "Viewport"); 400 } else if (tag == 0x0C020000) { 401 assert(size == AGX_LINKAGE_LENGTH); 402 DUMP_CL(LINKAGE, map, "Linkage"); 403 } else if (tag == 0x200004a) { 404 assert(size == AGX_UNKNOWN_4A_LENGTH); 405 DUMP_CL(UNKNOWN_4A, map, "Unknown 4a"); 406 } else if (tag == 0x10000b5) { 407 assert(size == AGX_RASTERIZER_LENGTH); 408 DUMP_CL(RASTERIZER, map, "Rasterizer"); 409 } else if (tag == 0x200000) { 410 assert(size == AGX_CULL_LENGTH); 411 DUMP_CL(CULL, map, "Cull"); 412 } else if (tag == 0x000100) { 413 assert(size == AGX_SET_INDEX_LENGTH); 414 DUMP_CL(SET_INDEX, map, "Set index"); 415 } else if (tag == 0x800000) { 416 assert(size == (AGX_BIND_PIPELINE_LENGTH - 4)); 417 418 agx_unpack(agxdecode_dump_stream, map, BIND_PIPELINE, cmd); 419 agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose); 420 421 /* TODO: parse */ 422 if (cmd.fs_varyings) { 423 uint8_t *map = agxdecode_fetch_gpu_mem(cmd.fs_varyings, 128); 424 hexdump(agxdecode_dump_stream, map, 128, false); 425 426 DUMP_CL(VARYING_HEADER, map, "Varying header:"); 427 map += AGX_VARYING_HEADER_LENGTH; 428 429 for (unsigned i = 0; i < cmd.input_count; ++i) { 430 DUMP_CL(VARYING, map, "Varying:"); 431 map += AGX_VARYING_LENGTH; 432 } 433 } 434 435 DUMP_UNPACKED(BIND_PIPELINE, cmd, "Bind fragment pipeline\n"); 436 } else if (size == 0) { 437 pipeline_base = va; 438 } else { 439 fprintf(agxdecode_dump_stream, "Record %" PRIx64 "\n", va); 440 hexdump(agxdecode_dump_stream, map, size, false); 441 } 442} 443 444static unsigned 445agxdecode_cmd(const uint8_t *map, bool verbose) 446{ 447 if (map[0] == 0x02 && map[1] == 0x10 && map[2] == 0x00 && map[3] == 0x00) { 448 agx_unpack(agxdecode_dump_stream, map, LAUNCH, cmd); 449 agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose); 450 DUMP_UNPACKED(LAUNCH, cmd, "Launch\n"); 451 return AGX_LAUNCH_LENGTH; 452 } else if (map[0] == 0x2E && map[1] == 0x00 && map[2] == 0x00 && map[3] == 0x40) { 453 agx_unpack(agxdecode_dump_stream, map, BIND_PIPELINE, cmd); 454 agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose); 455 DUMP_UNPACKED(BIND_PIPELINE, cmd, "Bind vertex pipeline\n"); 456 return AGX_BIND_PIPELINE_LENGTH; 457 } else if (map[3] == 0x61) { 458 DUMP_CL(DRAW, map, "Draw"); 459 return AGX_DRAW_LENGTH; 460 } else if (map[2] == 0x00 && map[3] == 0x00) { 461 /* No need to explicitly dump the record */ 462 agx_unpack(agxdecode_dump_stream, map, RECORD, cmd); 463 464 uint64_t address = (((uint64_t) cmd.pointer_hi) << 32) | cmd.pointer_lo; 465 struct agx_bo *mem = agxdecode_find_mapped_gpu_mem_containing(address); 466 467 if (mem) 468 agxdecode_record(address, cmd.size_words * 4, verbose); 469 else 470 DUMP_UNPACKED(RECORD, cmd, "Non-existant record (XXX)\n"); 471 472 return AGX_RECORD_LENGTH; 473 } else if (map[1] == 0 && map[2] == 0 && map[3] == 0xC0 && map[4] == 0x00) { 474 ASSERTED unsigned zero[4] = { 0 }; 475 assert(memcmp(map + 4, zero, sizeof(zero)) == 0); 476 return STATE_DONE; 477 } else { 478 return 0; 479 } 480} 481 482void 483agxdecode_cmdstream(unsigned cmdbuf_handle, unsigned map_handle, bool verbose) 484{ 485 agxdecode_dump_file_open(); 486 487 struct agx_bo *cmdbuf = agxdecode_find_handle(cmdbuf_handle, AGX_ALLOC_CMDBUF); 488 struct agx_bo *map = agxdecode_find_handle(map_handle, AGX_ALLOC_MEMMAP); 489 assert(cmdbuf != NULL && "nonexistant command buffer"); 490 assert(map != NULL && "nonexistant mapping"); 491 492 /* Before decoding anything, validate the map. Set bo->mapped fields */ 493 agxdecode_decode_segment_list(map->ptr.cpu); 494 495 /* Print the IOGPU stuff */ 496 agx_unpack(agxdecode_dump_stream, cmdbuf->ptr.cpu, IOGPU_HEADER, cmd); 497 DUMP_UNPACKED(IOGPU_HEADER, cmd, "IOGPU Header\n"); 498 agx_unpack(agxdecode_dump_stream, ((uint32_t *) cmdbuf->ptr.cpu) + 160, 499 IOGPU_INTERNAL_PIPELINES, pip); 500 501 DUMP_CL(IOGPU_INTERNAL_PIPELINES, ((uint32_t *) cmdbuf->ptr.cpu) + 160, "Internal pipelines"); 502 DUMP_CL(IOGPU_AUX_FRAMEBUFFER, ((uint32_t *) cmdbuf->ptr.cpu) + 228, "Aux Framebuffer"); 503 504 agx_unpack(agxdecode_dump_stream, ((uint32_t *) cmdbuf->ptr.cpu) + 292, 505 IOGPU_CLEAR_Z_S, clearzs); 506 DUMP_UNPACKED(IOGPU_CLEAR_Z_S, clearzs, "Clear Z/S"); 507 508 /* Guard against changes */ 509 uint32_t zeroes[356 - 344] = { 0 }; 510 assert(memcmp(((uint32_t *) cmdbuf->ptr.cpu) + 344, zeroes, 4 * (356 - 344)) == 0); 511 512 DUMP_CL(IOGPU_MISC, ((uint32_t *) cmdbuf->ptr.cpu) + 356, "Misc"); 513 514 /* Should be unused, we think */ 515 for (unsigned i = (0x6B0 / 4); i < (cmd.attachment_offset / 4); ++i) { 516 assert(((uint32_t *) cmdbuf->ptr.cpu)[i] == 0); 517 } 518 519 DUMP_CL(IOGPU_ATTACHMENT_COUNT, ((uint8_t *) cmdbuf->ptr.cpu + 520 cmd.attachment_offset), "Attachment count"); 521 522 uint32_t *attachments = (uint32_t *) ((uint8_t *) cmdbuf->ptr.cpu + cmd.attachment_offset); 523 unsigned attachment_count = attachments[3]; 524 for (unsigned i = 0; i < attachment_count; ++i) { 525 uint32_t *ptr = attachments + 4 + (i * AGX_IOGPU_ATTACHMENT_LENGTH / 4); 526 DUMP_CL(IOGPU_ATTACHMENT, ptr, "Attachment"); 527 } 528 529 uint64_t *encoder = ((uint64_t *) cmdbuf->ptr.cpu) + 7; 530 agxdecode_stateful(*encoder, "Encoder", agxdecode_cmd, verbose); 531 532 if (pip.clear_pipeline_unk) { 533 assert(pip.clear_pipeline_unk == 0x4); 534 agxdecode_stateful(pip.clear_pipeline, "Clear pipeline", 535 agxdecode_pipeline, verbose); 536 } 537 538 if (pip.store_pipeline_unk) { 539 assert(pip.store_pipeline_unk == 0x4); 540 agxdecode_stateful(pip.store_pipeline, "Store pipeline", 541 agxdecode_pipeline, verbose); 542 } 543 544 assert((clearzs.partial_reload_pipeline_unk & 0xF) == 0x4); 545 if (clearzs.partial_reload_pipeline) { 546 agxdecode_stateful(clearzs.partial_reload_pipeline, 547 "Partial reload pipeline", agxdecode_pipeline, verbose); 548 } 549 550 if (clearzs.partial_store_pipeline) { 551 agxdecode_stateful(clearzs.partial_store_pipeline, 552 "Partial store pipeline", agxdecode_pipeline, verbose); 553 } 554 555 agxdecode_map_read_write(); 556} 557 558void 559agxdecode_dump_mappings(unsigned map_handle) 560{ 561 agxdecode_dump_file_open(); 562 563 struct agx_bo *map = agxdecode_find_handle(map_handle, AGX_ALLOC_MEMMAP); 564 assert(map != NULL && "nonexistant mapping"); 565 agxdecode_decode_segment_list(map->ptr.cpu); 566 567 for (unsigned i = 0; i < mmap_count; ++i) { 568 if (!mmap_array[i].ptr.cpu || !mmap_array[i].size || !mmap_array[i].mapped) 569 continue; 570 571 assert(mmap_array[i].type < AGX_NUM_ALLOC); 572 573 fprintf(agxdecode_dump_stream, "Buffer: type %s, gpu %" PRIx64 ", handle %u.bin:\n\n", 574 agx_alloc_types[mmap_array[i].type], 575 mmap_array[i].ptr.gpu, mmap_array[i].handle); 576 577 hexdump(agxdecode_dump_stream, mmap_array[i].ptr.cpu, mmap_array[i].size, false); 578 fprintf(agxdecode_dump_stream, "\n"); 579 } 580} 581 582void 583agxdecode_track_alloc(struct agx_bo *alloc) 584{ 585 assert((mmap_count + 1) < MAX_MAPPINGS); 586 587 for (unsigned i = 0; i < mmap_count; ++i) { 588 struct agx_bo *bo = &mmap_array[i]; 589 bool match = (bo->handle == alloc->handle && bo->type == alloc->type); 590 assert(!match && "tried to alloc already allocated BO"); 591 } 592 593 mmap_array[mmap_count++] = *alloc; 594} 595 596void 597agxdecode_track_free(struct agx_bo *bo) 598{ 599 bool found = false; 600 601 for (unsigned i = 0; i < mmap_count; ++i) { 602 if (mmap_array[i].handle == bo->handle && mmap_array[i].type == bo->type) { 603 assert(!found && "mapped multiple times!"); 604 found = true; 605 606 memset(&mmap_array[i], 0, sizeof(mmap_array[i])); 607 } 608 } 609 610 assert(found && "freed unmapped memory"); 611} 612 613static int agxdecode_dump_frame_count = 0; 614 615void 616agxdecode_dump_file_open(void) 617{ 618 if (agxdecode_dump_stream) 619 return; 620 621 /* This does a getenv every frame, so it is possible to use 622 * setenv to change the base at runtime. 623 */ 624 const char *dump_file_base = getenv("AGXDECODE_DUMP_FILE") ?: "agxdecode.dump"; 625 if (!strcmp(dump_file_base, "stderr")) 626 agxdecode_dump_stream = stderr; 627 else { 628 char buffer[1024]; 629 snprintf(buffer, sizeof(buffer), "%s.%04d", dump_file_base, agxdecode_dump_frame_count); 630 printf("agxdecode: dump command stream to file %s\n", buffer); 631 agxdecode_dump_stream = fopen(buffer, "w"); 632 if (!agxdecode_dump_stream) 633 fprintf(stderr, 634 "agxdecode: failed to open command stream log file %s\n", 635 buffer); 636 } 637} 638 639static void 640agxdecode_dump_file_close(void) 641{ 642 if (agxdecode_dump_stream && agxdecode_dump_stream != stderr) { 643 fclose(agxdecode_dump_stream); 644 agxdecode_dump_stream = NULL; 645 } 646} 647 648void 649agxdecode_next_frame(void) 650{ 651 agxdecode_dump_file_close(); 652 agxdecode_dump_frame_count++; 653} 654 655void 656agxdecode_close(void) 657{ 658 agxdecode_dump_file_close(); 659} 660