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