• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2012 Rob Clark <robdclark@gmail.com>
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include <assert.h>
7 #include <ctype.h>
8 #include <err.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <getopt.h>
12 #include <signal.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <inttypes.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <sys/wait.h>
24 
25 #include "buffers.h"
26 #include "cffdec.h"
27 #include "disasm.h"
28 #include "io.h"
29 #include "pager.h"
30 #include "redump.h"
31 #include "rnnutil.h"
32 #include "script.h"
33 #include "rdutil.h"
34 
35 static struct cffdec_options options;
36 
37 static bool needs_wfi = false;
38 static bool is_blob = false;
39 static int show_comp = false;
40 static int interactive;
41 static int vertices;
42 static const char *exename;
43 
44 static int handle_file(const char *filename, int start, int end, int draw);
45 
46 static void
print_usage(const char * name)47 print_usage(const char *name)
48 {
49    /* clang-format off */
50    fprintf(stderr, "Usage:\n\n"
51            "\t%s [OPTIONS]... FILE...\n\n"
52            "Options:\n"
53            "\t-v, --verbose    - more verbose disassembly\n"
54            "\t--dump-shaders   - dump each shader to a raw file\n"
55            "\t--no-color       - disable colorized output (default for non-console\n"
56            "\t                   output)\n"
57            "\t--color          - enable colorized output (default for tty output)\n"
58            "\t--no-pager       - disable pager (default for non-console output)\n"
59            "\t--pager          - enable pager (default for tty output)\n"
60            "\t-s, --summary    - don't show individual register writes, but just\n"
61            "\t                   register values on draws\n"
62            "\t-a, --allregs    - show all registers (including ones not written\n"
63            "\t                   since previous draw) on each draw\n"
64            "\t-S, --start=N    - start decoding from frame N\n"
65            "\t-E, --end=N      - stop decoding after frame N\n"
66            "\t-F, --frame=N    - decode only frame N\n"
67            "\t-D, --draw=N     - decode only draw N\n"
68            "\t-e, --exe=NAME   - only decode cmdstream from named process\n"
69            "\t--textures       - dump texture contents (if possible)\n"
70            "\t--bindless       - dump bindless descriptors contents (if possible)\n"
71            "\t-L, --script=LUA - run specified lua script to analyze state\n"
72            "\t-q, --query=REG  - query mode, dump only specified query registers on\n"
73            "\t                   each draw; multiple --query/-q args can be given to\n"
74            "\t                   dump multiple registers; register can be specified\n"
75            "\t                   either by name or numeric offset\n"
76            "\t--query-all      - in query mode, show all queried regs on each draw\n"
77            "\t                   (default query mode)\n"
78            "\t--query-written  - in query mode, show queried regs on draws if any of\n"
79            "\t                   them have been written since previous draw\n"
80            "\t--query-delta    - in query mode, show queried regs on draws if any of\n"
81            "\t                   them have changed since previous draw\n"
82            "\t--query-compare  - dump registers for BINNING vs GMEM/BYPASS per draw;\n"
83            "\t                   only applicable for regs set via SDS group (a6xx+),\n"
84            "\t                   implies --once, can be combined with --query-all,\n"
85            "\t                   --query-written, or --query-delta\n"
86            "\t--once           - decode cmdstream only once (per draw mode); if same\n"
87            "\t                   cmdstream is executed for each tile, this will decode\n"
88            "\t                   it only for the first tile and skip the remainder,\n"
89            "\t                   which can be useful when looking at state that does\n"
90            "\t                   not change per tile\n"
91            "\t--not-once       - decode cmdstream for each IB (default)\n"
92            "\t--unit-test      - make reproducible output for unit testing\n"
93            "\t-h, --help       - show this message\n"
94            , name);
95    /* clang-format on */
96    exit(2);
97 }
98 
99 /* clang-format off */
100 static const struct option opts[] = {
101       /* Long opts that simply set a flag (no corresponding short alias: */
102       { "dump-shaders",    no_argument, &options.dump_shaders,  1 },
103       { "no-color",        no_argument, &options.color,         0 },
104       { "color",           no_argument, &options.color,         1 },
105       { "no-pager",        no_argument, &interactive,           0 },
106       { "pager",           no_argument, &interactive,           1 },
107       { "textures",        no_argument, &options.dump_textures, 1 },
108       { "bindless",        no_argument, &options.dump_bindless, 1 },
109       { "show-compositor", no_argument, &show_comp,             1 },
110       { "query-all",       no_argument, &options.query_mode,    QUERY_ALL },
111       { "query-written",   no_argument, &options.query_mode,    QUERY_WRITTEN },
112       { "query-delta",     no_argument, &options.query_mode,    QUERY_DELTA },
113       { "query-compare",   no_argument, &options.query_compare, 1 },
114       { "once",            no_argument, &options.once,          1 },
115       { "not-once",        no_argument, &options.once,          0 },
116       { "unit-test",       no_argument, &options.unit_test,     1 },
117 
118       /* Long opts with short alias: */
119       { "verbose",   no_argument,       0, 'v' },
120       { "summary",   no_argument,       0, 's' },
121       { "allregs",   no_argument,       0, 'a' },
122       { "start",     required_argument, 0, 'S' },
123       { "end",       required_argument, 0, 'E' },
124       { "frame",     required_argument, 0, 'F' },
125       { "draw",      required_argument, 0, 'D' },
126       { "exe",       required_argument, 0, 'e' },
127       { "script",    required_argument, 0, 'L' },
128       { "query",     required_argument, 0, 'q' },
129       { "help",      no_argument,       0, 'h' },
130 };
131 /* clang-format on */
132 
133 int
main(int argc,char ** argv)134 main(int argc, char **argv)
135 {
136    enum debug_t debug = PRINT_RAW | PRINT_STATS;
137    int ret = -1;
138    int start = 0, end = 0x7ffffff, draw = -1;
139    int c;
140 
141    interactive = isatty(STDOUT_FILENO);
142 
143    options.color = interactive;
144 
145    while ((c = getopt_long(argc, argv, "vsaS:E:F:D:e:L:q:h", opts, NULL)) !=
146           -1) {
147       switch (c) {
148       case 0:
149          /* option that set a flag, nothing to do */
150          break;
151       case 'v':
152          debug |= (PRINT_RAW | EXPAND_REPEAT | PRINT_VERBOSE);
153          break;
154       case 's':
155          options.summary = true;
156          break;
157       case 'a':
158          options.allregs = true;
159          break;
160       case 'S':
161          start = atoi(optarg);
162          break;
163       case 'E':
164          end = atoi(optarg);
165          break;
166       case 'F':
167          start = end = atoi(optarg);
168          break;
169       case 'D':
170          draw = atoi(optarg);
171          break;
172       case 'e':
173          exename = optarg;
174          break;
175       case 'L':
176          options.script = optarg;
177          if (script_load(options.script)) {
178             errx(-1, "error loading %s\n", options.script);
179          }
180          break;
181       case 'q':
182          options.querystrs =
183             realloc(options.querystrs,
184                     (options.nquery + 1) * sizeof(*options.querystrs));
185          options.querystrs[options.nquery] = optarg;
186          options.nquery++;
187          interactive = 0;
188          break;
189       case 'h':
190       default:
191          print_usage(argv[0]);
192       }
193    }
194 
195    disasm_a2xx_set_debug(debug);
196    disasm_a3xx_set_debug(debug);
197 
198    if (interactive) {
199       pager_open();
200    }
201 
202    while (optind < argc) {
203       ret = handle_file(argv[optind], start, end, draw);
204       if (ret) {
205          fprintf(stderr, "error reading: %s\n", argv[optind]);
206          fprintf(stderr, "continuing..\n");
207       }
208       optind++;
209    }
210 
211    if (ret)
212       print_usage(argv[0]);
213 
214    if ((options.query_mode || options.query_compare) && !options.nquery) {
215       fprintf(stderr, "query options only valid in query mode!\n");
216       print_usage(argv[0]);
217    }
218 
219    script_finish();
220 
221    if (interactive) {
222       pager_close();
223    }
224 
225    return ret;
226 }
227 
228 static int
handle_file(const char * filename,int start,int end,int draw)229 handle_file(const char *filename, int start, int end, int draw)
230 {
231    struct io *io;
232    int submit = 0, got_gpu_id = 0;
233    bool needs_reset = false;
234    bool skip = false;
235    struct rd_parsed_section ps = {0};
236 
237    options.draw_filter = draw;
238 
239    cffdec_init(&options);
240 
241    if (!options.unit_test)
242       printf("Reading %s...\n", filename);
243 
244    script_start_cmdstream(filename);
245 
246    if (!strcmp(filename, "-"))
247       io = io_openfd(0);
248    else
249       io = io_open(filename);
250 
251    if (!io) {
252       fprintf(stderr, "could not open: %s\n", filename);
253       return -1;
254    }
255 
256    struct {
257       unsigned int len;
258       uint64_t gpuaddr;
259    } gpuaddr = {0};
260 
261    while (parse_rd_section(io, &ps)) {
262       needs_wfi = false;
263 
264       switch (ps.type) {
265       case RD_TEST:
266          printl(1, "test: %s\n", (char *)ps.buf);
267          break;
268       case RD_CMD:
269          is_blob = true;
270          printl(2, "cmd: %s\n", (char *)ps.buf);
271          skip = false;
272          if (exename) {
273             skip |= (strstr(ps.buf, exename) != ps.buf);
274          } else if (!show_comp) {
275             skip |= (strstr(ps.buf, "fdperf") == ps.buf);
276             skip |= (strstr(ps.buf, "chrome") == ps.buf);
277             skip |= (strstr(ps.buf, "surfaceflinger") == ps.buf);
278             skip |= ((char *)ps.buf)[0] == 'X';
279          }
280          break;
281       case RD_VERT_SHADER:
282          printl(2, "vertex shader:\n%s\n", (char *)ps.buf);
283          break;
284       case RD_FRAG_SHADER:
285          printl(2, "fragment shader:\n%s\n", (char *)ps.buf);
286          break;
287       case RD_GPUADDR:
288          if (needs_reset) {
289             reset_buffers();
290             needs_reset = false;
291          }
292          parse_addr(ps.buf, ps.sz, &gpuaddr.len, &gpuaddr.gpuaddr);
293          break;
294       case RD_BUFFER_CONTENTS:
295          add_buffer(gpuaddr.gpuaddr, gpuaddr.len, ps.buf);
296          ps.buf = NULL;
297          break;
298       case RD_CMDSTREAM_ADDR:
299          if ((start <= submit) && (submit <= end)) {
300             unsigned int sizedwords;
301             uint64_t gpuaddr;
302             parse_addr(ps.buf, ps.sz, &sizedwords, &gpuaddr);
303             printl(2, "############################################################\n");
304             printl(2, "cmdstream[%d]: %d dwords\n", submit, sizedwords);
305             if (!skip) {
306                script_start_submit();
307                dump_commands(hostptr(gpuaddr), sizedwords, 0);
308                script_end_submit();
309             }
310             printl(2, "############################################################\n");
311             printl(2, "vertices: %d\n", vertices);
312          }
313          needs_reset = true;
314          submit++;
315          break;
316       case RD_GPU_ID:
317          if (!got_gpu_id) {
318             uint32_t gpu_id = parse_gpu_id(ps.buf);
319             if (!gpu_id)
320                break;
321             options.dev_id.gpu_id = gpu_id;
322             printl(2, "gpu_id: %d\n", options.dev_id.gpu_id);
323 
324             options.info = fd_dev_info_raw(&options.dev_id);
325             if (!options.info)
326                break;
327 
328             cffdec_init(&options);
329             got_gpu_id = 1;
330          }
331          break;
332       case RD_CHIP_ID:
333          if (!got_gpu_id) {
334             options.dev_id.chip_id = parse_chip_id(ps.buf);
335             printl(2, "chip_id: 0x%" PRIx64 "\n", options.dev_id.chip_id);
336 
337             options.info = fd_dev_info_raw(&options.dev_id);
338             if (!options.info)
339                break;
340 
341             cffdec_init(&options);
342             got_gpu_id = 1;
343          }
344          break;
345       default:
346          break;
347       }
348    }
349 
350    script_end_cmdstream();
351 
352    reset_buffers();
353 
354    io_close(io);
355    fflush(stdout);
356 
357    if (ps.ret < 0) {
358       printf("corrupt file\n");
359    }
360    return 0;
361 }
362