17ec681f3Smrg/* 27ec681f3Smrg * Copyright (c) 2012 Rob Clark <robdclark@gmail.com> 37ec681f3Smrg * 47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 57ec681f3Smrg * copy of this software and associated documentation files (the "Software"), 67ec681f3Smrg * to deal in the Software without restriction, including without limitation 77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the 97ec681f3Smrg * Software is furnished to do so, subject to the following conditions: 107ec681f3Smrg * 117ec681f3Smrg * The above copyright notice and this permission notice (including the next 127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the 137ec681f3Smrg * Software. 147ec681f3Smrg * 157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 207ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 217ec681f3Smrg * SOFTWARE. 227ec681f3Smrg */ 237ec681f3Smrg 247ec681f3Smrg#include <assert.h> 257ec681f3Smrg#include <ctype.h> 267ec681f3Smrg#include <err.h> 277ec681f3Smrg#include <errno.h> 287ec681f3Smrg#include <fcntl.h> 297ec681f3Smrg#include <getopt.h> 307ec681f3Smrg#include <signal.h> 317ec681f3Smrg#include <stdarg.h> 327ec681f3Smrg#include <stdbool.h> 337ec681f3Smrg#include <stdint.h> 347ec681f3Smrg#include <stdio.h> 357ec681f3Smrg#include <stdlib.h> 367ec681f3Smrg#include <string.h> 377ec681f3Smrg#include <unistd.h> 387ec681f3Smrg#include <sys/stat.h> 397ec681f3Smrg#include <sys/types.h> 407ec681f3Smrg#include <sys/wait.h> 417ec681f3Smrg 427ec681f3Smrg#include "buffers.h" 437ec681f3Smrg#include "cffdec.h" 447ec681f3Smrg#include "disasm.h" 457ec681f3Smrg#include "io.h" 467ec681f3Smrg#include "pager.h" 477ec681f3Smrg#include "redump.h" 487ec681f3Smrg#include "rnnutil.h" 497ec681f3Smrg#include "script.h" 507ec681f3Smrg 517ec681f3Smrgstatic struct cffdec_options options = { 527ec681f3Smrg .gpu_id = 220, 537ec681f3Smrg}; 547ec681f3Smrg 557ec681f3Smrgstatic bool needs_wfi = false; 567ec681f3Smrgstatic bool is_blob = false; 577ec681f3Smrgstatic int show_comp = false; 587ec681f3Smrgstatic int interactive; 597ec681f3Smrgstatic int vertices; 607ec681f3Smrgstatic const char *exename; 617ec681f3Smrg 627ec681f3Smrgstatic int handle_file(const char *filename, int start, int end, int draw); 637ec681f3Smrg 647ec681f3Smrgstatic void 657ec681f3Smrgprint_usage(const char *name) 667ec681f3Smrg{ 677ec681f3Smrg /* clang-format off */ 687ec681f3Smrg fprintf(stderr, "Usage:\n\n" 697ec681f3Smrg "\t%s [OPTSIONS]... FILE...\n\n" 707ec681f3Smrg "Options:\n" 717ec681f3Smrg "\t-v, --verbose - more verbose disassembly\n" 727ec681f3Smrg "\t--dump-shaders - dump each shader to a raw file\n" 737ec681f3Smrg "\t--no-color - disable colorized output (default for non-console\n" 747ec681f3Smrg "\t output)\n" 757ec681f3Smrg "\t--color - enable colorized output (default for tty output)\n" 767ec681f3Smrg "\t--no-pager - disable pager (default for non-console output)\n" 777ec681f3Smrg "\t--pager - enable pager (default for tty output)\n" 787ec681f3Smrg "\t-s, --summary - don't show individual register writes, but just\n" 797ec681f3Smrg "\t register values on draws\n" 807ec681f3Smrg "\t-a, --allregs - show all registers (including ones not written\n" 817ec681f3Smrg "\t since previous draw) on each draw\n" 827ec681f3Smrg "\t-S, --start=N - start decoding from frame N\n" 837ec681f3Smrg "\t-E, --end=N - stop decoding after frame N\n" 847ec681f3Smrg "\t-F, --frame=N - decode only frame N\n" 857ec681f3Smrg "\t-D, --draw=N - decode only draw N\n" 867ec681f3Smrg "\t-e, --exe=NAME - only decode cmdstream from named process\n" 877ec681f3Smrg "\t--textures - dump texture contents (if possible)\n" 887ec681f3Smrg "\t-L, --script=LUA - run specified lua script to analyze state\n" 897ec681f3Smrg "\t-q, --query=REG - query mode, dump only specified query registers on\n" 907ec681f3Smrg "\t each draw; multiple --query/-q args can be given to\n" 917ec681f3Smrg "\t dump multiple registers; register can be specified\n" 927ec681f3Smrg "\t either by name or numeric offset\n" 937ec681f3Smrg "\t--query-all - in query mode, show all queried regs on each draw\n" 947ec681f3Smrg "\t (default query mode)\n" 957ec681f3Smrg "\t--query-written - in query mode, show queried regs on draws if any of\n" 967ec681f3Smrg "\t them have been written since previous draw\n" 977ec681f3Smrg "\t--query-delta - in query mode, show queried regs on draws if any of\n" 987ec681f3Smrg "\t them have changed since previous draw\n" 997ec681f3Smrg "\t--query-compare - dump registers for BINNING vs GMEM/BYPASS per draw;\n" 1007ec681f3Smrg "\t only applicable for regs set via SDS group (a6xx+),\n" 1017ec681f3Smrg "\t implies --once, can be combined with --query-all,\n" 1027ec681f3Smrg "\t --query-written, or --query-delta\n" 1037ec681f3Smrg "\t--once - decode cmdstream only once (per draw mode); if same\n" 1047ec681f3Smrg "\t cmdstream is executed for each tile, this will decode\n" 1057ec681f3Smrg "\t it only for the first tile and skip the remainder,\n" 1067ec681f3Smrg "\t which can be useful when looking at state that does\n" 1077ec681f3Smrg "\t not change per tile\n" 1087ec681f3Smrg "\t--not-once - decode cmdstream for each IB (default)\n" 1097ec681f3Smrg "\t--unit-test - make reproducible output for unit testing\n" 1107ec681f3Smrg "\t-h, --help - show this message\n" 1117ec681f3Smrg , name); 1127ec681f3Smrg /* clang-format on */ 1137ec681f3Smrg exit(2); 1147ec681f3Smrg} 1157ec681f3Smrg 1167ec681f3Smrg/* clang-format off */ 1177ec681f3Smrgstatic const struct option opts[] = { 1187ec681f3Smrg /* Long opts that simply set a flag (no corresponding short alias: */ 1197ec681f3Smrg { "dump-shaders", no_argument, &options.dump_shaders, 1 }, 1207ec681f3Smrg { "no-color", no_argument, &options.color, 0 }, 1217ec681f3Smrg { "color", no_argument, &options.color, 1 }, 1227ec681f3Smrg { "no-pager", no_argument, &interactive, 0 }, 1237ec681f3Smrg { "pager", no_argument, &interactive, 1 }, 1247ec681f3Smrg { "textures", no_argument, &options.dump_textures, 1 }, 1257ec681f3Smrg { "show-compositor", no_argument, &show_comp, 1 }, 1267ec681f3Smrg { "query-all", no_argument, &options.query_mode, QUERY_ALL }, 1277ec681f3Smrg { "query-written", no_argument, &options.query_mode, QUERY_WRITTEN }, 1287ec681f3Smrg { "query-delta", no_argument, &options.query_mode, QUERY_DELTA }, 1297ec681f3Smrg { "query-compare", no_argument, &options.query_compare, 1 }, 1307ec681f3Smrg { "once", no_argument, &options.once, 1 }, 1317ec681f3Smrg { "not-once", no_argument, &options.once, 0 }, 1327ec681f3Smrg { "unit-test", no_argument, &options.unit_test, 1 }, 1337ec681f3Smrg 1347ec681f3Smrg /* Long opts with short alias: */ 1357ec681f3Smrg { "verbose", no_argument, 0, 'v' }, 1367ec681f3Smrg { "summary", no_argument, 0, 's' }, 1377ec681f3Smrg { "allregs", no_argument, 0, 'a' }, 1387ec681f3Smrg { "start", required_argument, 0, 'S' }, 1397ec681f3Smrg { "end", required_argument, 0, 'E' }, 1407ec681f3Smrg { "frame", required_argument, 0, 'F' }, 1417ec681f3Smrg { "draw", required_argument, 0, 'D' }, 1427ec681f3Smrg { "exe", required_argument, 0, 'e' }, 1437ec681f3Smrg { "script", required_argument, 0, 'L' }, 1447ec681f3Smrg { "query", required_argument, 0, 'q' }, 1457ec681f3Smrg { "help", no_argument, 0, 'h' }, 1467ec681f3Smrg}; 1477ec681f3Smrg/* clang-format on */ 1487ec681f3Smrg 1497ec681f3Smrgint 1507ec681f3Smrgmain(int argc, char **argv) 1517ec681f3Smrg{ 1527ec681f3Smrg enum debug_t debug = PRINT_RAW | PRINT_STATS; 1537ec681f3Smrg int ret = -1; 1547ec681f3Smrg int start = 0, end = 0x7ffffff, draw = -1; 1557ec681f3Smrg int c; 1567ec681f3Smrg 1577ec681f3Smrg interactive = isatty(STDOUT_FILENO); 1587ec681f3Smrg 1597ec681f3Smrg options.color = interactive; 1607ec681f3Smrg 1617ec681f3Smrg while ((c = getopt_long(argc, argv, "vsaS:E:F:D:e:L:q:h", opts, NULL)) != 1627ec681f3Smrg -1) { 1637ec681f3Smrg switch (c) { 1647ec681f3Smrg case 0: 1657ec681f3Smrg /* option that set a flag, nothing to do */ 1667ec681f3Smrg break; 1677ec681f3Smrg case 'v': 1687ec681f3Smrg debug |= (PRINT_RAW | EXPAND_REPEAT | PRINT_VERBOSE); 1697ec681f3Smrg break; 1707ec681f3Smrg case 's': 1717ec681f3Smrg options.summary = true; 1727ec681f3Smrg break; 1737ec681f3Smrg case 'a': 1747ec681f3Smrg options.allregs = true; 1757ec681f3Smrg break; 1767ec681f3Smrg case 'S': 1777ec681f3Smrg start = atoi(optarg); 1787ec681f3Smrg break; 1797ec681f3Smrg case 'E': 1807ec681f3Smrg end = atoi(optarg); 1817ec681f3Smrg break; 1827ec681f3Smrg case 'F': 1837ec681f3Smrg start = end = atoi(optarg); 1847ec681f3Smrg break; 1857ec681f3Smrg case 'D': 1867ec681f3Smrg draw = atoi(optarg); 1877ec681f3Smrg break; 1887ec681f3Smrg case 'e': 1897ec681f3Smrg exename = optarg; 1907ec681f3Smrg break; 1917ec681f3Smrg case 'L': 1927ec681f3Smrg options.script = optarg; 1937ec681f3Smrg if (script_load(options.script)) { 1947ec681f3Smrg errx(-1, "error loading %s\n", options.script); 1957ec681f3Smrg } 1967ec681f3Smrg break; 1977ec681f3Smrg case 'q': 1987ec681f3Smrg options.querystrs = 1997ec681f3Smrg realloc(options.querystrs, 2007ec681f3Smrg (options.nquery + 1) * sizeof(*options.querystrs)); 2017ec681f3Smrg options.querystrs[options.nquery] = optarg; 2027ec681f3Smrg options.nquery++; 2037ec681f3Smrg interactive = 0; 2047ec681f3Smrg break; 2057ec681f3Smrg case 'h': 2067ec681f3Smrg default: 2077ec681f3Smrg print_usage(argv[0]); 2087ec681f3Smrg } 2097ec681f3Smrg } 2107ec681f3Smrg 2117ec681f3Smrg disasm_a2xx_set_debug(debug); 2127ec681f3Smrg disasm_a3xx_set_debug(debug); 2137ec681f3Smrg 2147ec681f3Smrg if (interactive) { 2157ec681f3Smrg pager_open(); 2167ec681f3Smrg } 2177ec681f3Smrg 2187ec681f3Smrg while (optind < argc) { 2197ec681f3Smrg ret = handle_file(argv[optind], start, end, draw); 2207ec681f3Smrg if (ret) { 2217ec681f3Smrg fprintf(stderr, "error reading: %s\n", argv[optind]); 2227ec681f3Smrg fprintf(stderr, "continuing..\n"); 2237ec681f3Smrg } 2247ec681f3Smrg optind++; 2257ec681f3Smrg } 2267ec681f3Smrg 2277ec681f3Smrg if (ret) 2287ec681f3Smrg print_usage(argv[0]); 2297ec681f3Smrg 2307ec681f3Smrg if ((options.query_mode || options.query_compare) && !options.nquery) { 2317ec681f3Smrg fprintf(stderr, "query options only valid in query mode!\n"); 2327ec681f3Smrg print_usage(argv[0]); 2337ec681f3Smrg } 2347ec681f3Smrg 2357ec681f3Smrg script_finish(); 2367ec681f3Smrg 2377ec681f3Smrg if (interactive) { 2387ec681f3Smrg pager_close(); 2397ec681f3Smrg } 2407ec681f3Smrg 2417ec681f3Smrg return ret; 2427ec681f3Smrg} 2437ec681f3Smrg 2447ec681f3Smrgstatic void 2457ec681f3Smrgparse_addr(uint32_t *buf, int sz, unsigned int *len, uint64_t *gpuaddr) 2467ec681f3Smrg{ 2477ec681f3Smrg *gpuaddr = buf[0]; 2487ec681f3Smrg *len = buf[1]; 2497ec681f3Smrg if (sz > 8) 2507ec681f3Smrg *gpuaddr |= ((uint64_t)(buf[2])) << 32; 2517ec681f3Smrg} 2527ec681f3Smrg 2537ec681f3Smrgstatic int 2547ec681f3Smrghandle_file(const char *filename, int start, int end, int draw) 2557ec681f3Smrg{ 2567ec681f3Smrg enum rd_sect_type type = RD_NONE; 2577ec681f3Smrg void *buf = NULL; 2587ec681f3Smrg struct io *io; 2597ec681f3Smrg int submit = 0, got_gpu_id = 0; 2607ec681f3Smrg int sz, ret = 0; 2617ec681f3Smrg bool needs_reset = false; 2627ec681f3Smrg bool skip = false; 2637ec681f3Smrg 2647ec681f3Smrg options.draw_filter = draw; 2657ec681f3Smrg 2667ec681f3Smrg cffdec_init(&options); 2677ec681f3Smrg 2687ec681f3Smrg if (!options.unit_test) 2697ec681f3Smrg printf("Reading %s...\n", filename); 2707ec681f3Smrg 2717ec681f3Smrg script_start_cmdstream(filename); 2727ec681f3Smrg 2737ec681f3Smrg if (!strcmp(filename, "-")) 2747ec681f3Smrg io = io_openfd(0); 2757ec681f3Smrg else 2767ec681f3Smrg io = io_open(filename); 2777ec681f3Smrg 2787ec681f3Smrg if (!io) { 2797ec681f3Smrg fprintf(stderr, "could not open: %s\n", filename); 2807ec681f3Smrg return -1; 2817ec681f3Smrg } 2827ec681f3Smrg 2837ec681f3Smrg struct { 2847ec681f3Smrg unsigned int len; 2857ec681f3Smrg uint64_t gpuaddr; 2867ec681f3Smrg } gpuaddr = {0}; 2877ec681f3Smrg 2887ec681f3Smrg while (true) { 2897ec681f3Smrg uint32_t arr[2]; 2907ec681f3Smrg 2917ec681f3Smrg ret = io_readn(io, arr, 8); 2927ec681f3Smrg if (ret <= 0) 2937ec681f3Smrg goto end; 2947ec681f3Smrg 2957ec681f3Smrg while ((arr[0] == 0xffffffff) && (arr[1] == 0xffffffff)) { 2967ec681f3Smrg ret = io_readn(io, arr, 8); 2977ec681f3Smrg if (ret <= 0) 2987ec681f3Smrg goto end; 2997ec681f3Smrg } 3007ec681f3Smrg 3017ec681f3Smrg type = arr[0]; 3027ec681f3Smrg sz = arr[1]; 3037ec681f3Smrg 3047ec681f3Smrg if (sz < 0) { 3057ec681f3Smrg ret = -1; 3067ec681f3Smrg goto end; 3077ec681f3Smrg } 3087ec681f3Smrg 3097ec681f3Smrg free(buf); 3107ec681f3Smrg 3117ec681f3Smrg needs_wfi = false; 3127ec681f3Smrg 3137ec681f3Smrg buf = malloc(sz + 1); 3147ec681f3Smrg ((char *)buf)[sz] = '\0'; 3157ec681f3Smrg ret = io_readn(io, buf, sz); 3167ec681f3Smrg if (ret < 0) 3177ec681f3Smrg goto end; 3187ec681f3Smrg 3197ec681f3Smrg switch (type) { 3207ec681f3Smrg case RD_TEST: 3217ec681f3Smrg printl(1, "test: %s\n", (char *)buf); 3227ec681f3Smrg break; 3237ec681f3Smrg case RD_CMD: 3247ec681f3Smrg is_blob = true; 3257ec681f3Smrg printl(2, "cmd: %s\n", (char *)buf); 3267ec681f3Smrg skip = false; 3277ec681f3Smrg if (exename) { 3287ec681f3Smrg skip |= (strstr(buf, exename) != buf); 3297ec681f3Smrg } else if (!show_comp) { 3307ec681f3Smrg skip |= (strstr(buf, "fdperf") == buf); 3317ec681f3Smrg skip |= (strstr(buf, "chrome") == buf); 3327ec681f3Smrg skip |= (strstr(buf, "surfaceflinger") == buf); 3337ec681f3Smrg skip |= ((char *)buf)[0] == 'X'; 3347ec681f3Smrg } 3357ec681f3Smrg break; 3367ec681f3Smrg case RD_VERT_SHADER: 3377ec681f3Smrg printl(2, "vertex shader:\n%s\n", (char *)buf); 3387ec681f3Smrg break; 3397ec681f3Smrg case RD_FRAG_SHADER: 3407ec681f3Smrg printl(2, "fragment shader:\n%s\n", (char *)buf); 3417ec681f3Smrg break; 3427ec681f3Smrg case RD_GPUADDR: 3437ec681f3Smrg if (needs_reset) { 3447ec681f3Smrg reset_buffers(); 3457ec681f3Smrg needs_reset = false; 3467ec681f3Smrg } 3477ec681f3Smrg parse_addr(buf, sz, &gpuaddr.len, &gpuaddr.gpuaddr); 3487ec681f3Smrg break; 3497ec681f3Smrg case RD_BUFFER_CONTENTS: 3507ec681f3Smrg add_buffer(gpuaddr.gpuaddr, gpuaddr.len, buf); 3517ec681f3Smrg buf = NULL; 3527ec681f3Smrg break; 3537ec681f3Smrg case RD_CMDSTREAM_ADDR: 3547ec681f3Smrg if ((start <= submit) && (submit <= end)) { 3557ec681f3Smrg unsigned int sizedwords; 3567ec681f3Smrg uint64_t gpuaddr; 3577ec681f3Smrg parse_addr(buf, sz, &sizedwords, &gpuaddr); 3587ec681f3Smrg printl(2, "############################################################\n"); 3597ec681f3Smrg printl(2, "cmdstream: %d dwords\n", sizedwords); 3607ec681f3Smrg if (!skip) { 3617ec681f3Smrg script_start_submit(); 3627ec681f3Smrg dump_commands(hostptr(gpuaddr), sizedwords, 0); 3637ec681f3Smrg script_end_submit(); 3647ec681f3Smrg } 3657ec681f3Smrg printl(2, "############################################################\n"); 3667ec681f3Smrg printl(2, "vertices: %d\n", vertices); 3677ec681f3Smrg } 3687ec681f3Smrg needs_reset = true; 3697ec681f3Smrg submit++; 3707ec681f3Smrg break; 3717ec681f3Smrg case RD_GPU_ID: 3727ec681f3Smrg if (!got_gpu_id) { 3737ec681f3Smrg options.gpu_id = *((unsigned int *)buf); 3747ec681f3Smrg printl(2, "gpu_id: %d\n", options.gpu_id); 3757ec681f3Smrg cffdec_init(&options); 3767ec681f3Smrg got_gpu_id = 1; 3777ec681f3Smrg } 3787ec681f3Smrg break; 3797ec681f3Smrg default: 3807ec681f3Smrg break; 3817ec681f3Smrg } 3827ec681f3Smrg } 3837ec681f3Smrg 3847ec681f3Smrgend: 3857ec681f3Smrg script_end_cmdstream(); 3867ec681f3Smrg 3877ec681f3Smrg io_close(io); 3887ec681f3Smrg fflush(stdout); 3897ec681f3Smrg 3907ec681f3Smrg if (ret < 0) { 3917ec681f3Smrg printf("corrupt file\n"); 3927ec681f3Smrg } 3937ec681f3Smrg return 0; 3947ec681f3Smrg} 395