xref: /aosp_15_r20/external/mesa3d/src/freedreno/decode/cffdump.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2012 Rob Clark <[email protected]>
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-L, --script=LUA - run specified lua script to analyze state\n"
71            "\t-q, --query=REG  - query mode, dump only specified query registers on\n"
72            "\t                   each draw; multiple --query/-q args can be given to\n"
73            "\t                   dump multiple registers; register can be specified\n"
74            "\t                   either by name or numeric offset\n"
75            "\t--query-all      - in query mode, show all queried regs on each draw\n"
76            "\t                   (default query mode)\n"
77            "\t--query-written  - in query mode, show queried regs on draws if any of\n"
78            "\t                   them have been written since previous draw\n"
79            "\t--query-delta    - in query mode, show queried regs on draws if any of\n"
80            "\t                   them have changed since previous draw\n"
81            "\t--query-compare  - dump registers for BINNING vs GMEM/BYPASS per draw;\n"
82            "\t                   only applicable for regs set via SDS group (a6xx+),\n"
83            "\t                   implies --once, can be combined with --query-all,\n"
84            "\t                   --query-written, or --query-delta\n"
85            "\t--once           - decode cmdstream only once (per draw mode); if same\n"
86            "\t                   cmdstream is executed for each tile, this will decode\n"
87            "\t                   it only for the first tile and skip the remainder,\n"
88            "\t                   which can be useful when looking at state that does\n"
89            "\t                   not change per tile\n"
90            "\t--not-once       - decode cmdstream for each IB (default)\n"
91            "\t--unit-test      - make reproducible output for unit testing\n"
92            "\t-h, --help       - show this message\n"
93            , name);
94    /* clang-format on */
95    exit(2);
96 }
97 
98 /* clang-format off */
99 static const struct option opts[] = {
100       /* Long opts that simply set a flag (no corresponding short alias: */
101       { "dump-shaders",    no_argument, &options.dump_shaders,  1 },
102       { "no-color",        no_argument, &options.color,         0 },
103       { "color",           no_argument, &options.color,         1 },
104       { "no-pager",        no_argument, &interactive,           0 },
105       { "pager",           no_argument, &interactive,           1 },
106       { "textures",        no_argument, &options.dump_textures, 1 },
107       { "show-compositor", no_argument, &show_comp,             1 },
108       { "query-all",       no_argument, &options.query_mode,    QUERY_ALL },
109       { "query-written",   no_argument, &options.query_mode,    QUERY_WRITTEN },
110       { "query-delta",     no_argument, &options.query_mode,    QUERY_DELTA },
111       { "query-compare",   no_argument, &options.query_compare, 1 },
112       { "once",            no_argument, &options.once,          1 },
113       { "not-once",        no_argument, &options.once,          0 },
114       { "unit-test",       no_argument, &options.unit_test,     1 },
115 
116       /* Long opts with short alias: */
117       { "verbose",   no_argument,       0, 'v' },
118       { "summary",   no_argument,       0, 's' },
119       { "allregs",   no_argument,       0, 'a' },
120       { "start",     required_argument, 0, 'S' },
121       { "end",       required_argument, 0, 'E' },
122       { "frame",     required_argument, 0, 'F' },
123       { "draw",      required_argument, 0, 'D' },
124       { "exe",       required_argument, 0, 'e' },
125       { "script",    required_argument, 0, 'L' },
126       { "query",     required_argument, 0, 'q' },
127       { "help",      no_argument,       0, 'h' },
128 };
129 /* clang-format on */
130 
131 int
main(int argc,char ** argv)132 main(int argc, char **argv)
133 {
134    enum debug_t debug = PRINT_RAW | PRINT_STATS;
135    int ret = -1;
136    int start = 0, end = 0x7ffffff, draw = -1;
137    int c;
138 
139    interactive = isatty(STDOUT_FILENO);
140 
141    options.color = interactive;
142 
143    while ((c = getopt_long(argc, argv, "vsaS:E:F:D:e:L:q:h", opts, NULL)) !=
144           -1) {
145       switch (c) {
146       case 0:
147          /* option that set a flag, nothing to do */
148          break;
149       case 'v':
150          debug |= (PRINT_RAW | EXPAND_REPEAT | PRINT_VERBOSE);
151          break;
152       case 's':
153          options.summary = true;
154          break;
155       case 'a':
156          options.allregs = true;
157          break;
158       case 'S':
159          start = atoi(optarg);
160          break;
161       case 'E':
162          end = atoi(optarg);
163          break;
164       case 'F':
165          start = end = atoi(optarg);
166          break;
167       case 'D':
168          draw = atoi(optarg);
169          break;
170       case 'e':
171          exename = optarg;
172          break;
173       case 'L':
174          options.script = optarg;
175          if (script_load(options.script)) {
176             errx(-1, "error loading %s\n", options.script);
177          }
178          break;
179       case 'q':
180          options.querystrs =
181             realloc(options.querystrs,
182                     (options.nquery + 1) * sizeof(*options.querystrs));
183          options.querystrs[options.nquery] = optarg;
184          options.nquery++;
185          interactive = 0;
186          break;
187       case 'h':
188       default:
189          print_usage(argv[0]);
190       }
191    }
192 
193    disasm_a2xx_set_debug(debug);
194    disasm_a3xx_set_debug(debug);
195 
196    if (interactive) {
197       pager_open();
198    }
199 
200    while (optind < argc) {
201       ret = handle_file(argv[optind], start, end, draw);
202       if (ret) {
203          fprintf(stderr, "error reading: %s\n", argv[optind]);
204          fprintf(stderr, "continuing..\n");
205       }
206       optind++;
207    }
208 
209    if (ret)
210       print_usage(argv[0]);
211 
212    if ((options.query_mode || options.query_compare) && !options.nquery) {
213       fprintf(stderr, "query options only valid in query mode!\n");
214       print_usage(argv[0]);
215    }
216 
217    script_finish();
218 
219    if (interactive) {
220       pager_close();
221    }
222 
223    return ret;
224 }
225 
226 static int
handle_file(const char * filename,int start,int end,int draw)227 handle_file(const char *filename, int start, int end, int draw)
228 {
229    struct io *io;
230    int submit = 0, got_gpu_id = 0;
231    bool needs_reset = false;
232    bool skip = false;
233    struct rd_parsed_section ps = {0};
234 
235    options.draw_filter = draw;
236 
237    cffdec_init(&options);
238 
239    if (!options.unit_test)
240       printf("Reading %s...\n", filename);
241 
242    script_start_cmdstream(filename);
243 
244    if (!strcmp(filename, "-"))
245       io = io_openfd(0);
246    else
247       io = io_open(filename);
248 
249    if (!io) {
250       fprintf(stderr, "could not open: %s\n", filename);
251       return -1;
252    }
253 
254    struct {
255       unsigned int len;
256       uint64_t gpuaddr;
257    } gpuaddr = {0};
258 
259    while (parse_rd_section(io, &ps)) {
260       needs_wfi = false;
261 
262       switch (ps.type) {
263       case RD_TEST:
264          printl(1, "test: %s\n", (char *)ps.buf);
265          break;
266       case RD_CMD:
267          is_blob = true;
268          printl(2, "cmd: %s\n", (char *)ps.buf);
269          skip = false;
270          if (exename) {
271             skip |= (strstr(ps.buf, exename) != ps.buf);
272          } else if (!show_comp) {
273             skip |= (strstr(ps.buf, "fdperf") == ps.buf);
274             skip |= (strstr(ps.buf, "chrome") == ps.buf);
275             skip |= (strstr(ps.buf, "surfaceflinger") == ps.buf);
276             skip |= ((char *)ps.buf)[0] == 'X';
277          }
278          break;
279       case RD_VERT_SHADER:
280          printl(2, "vertex shader:\n%s\n", (char *)ps.buf);
281          break;
282       case RD_FRAG_SHADER:
283          printl(2, "fragment shader:\n%s\n", (char *)ps.buf);
284          break;
285       case RD_GPUADDR:
286          if (needs_reset) {
287             reset_buffers();
288             needs_reset = false;
289          }
290          parse_addr(ps.buf, ps.sz, &gpuaddr.len, &gpuaddr.gpuaddr);
291          break;
292       case RD_BUFFER_CONTENTS:
293          add_buffer(gpuaddr.gpuaddr, gpuaddr.len, ps.buf);
294          ps.buf = NULL;
295          break;
296       case RD_CMDSTREAM_ADDR:
297          if ((start <= submit) && (submit <= end)) {
298             unsigned int sizedwords;
299             uint64_t gpuaddr;
300             parse_addr(ps.buf, ps.sz, &sizedwords, &gpuaddr);
301             printl(2, "############################################################\n");
302             printl(2, "cmdstream[%d]: %d dwords\n", submit, sizedwords);
303             if (!skip) {
304                script_start_submit();
305                dump_commands(hostptr(gpuaddr), sizedwords, 0);
306                script_end_submit();
307             }
308             printl(2, "############################################################\n");
309             printl(2, "vertices: %d\n", vertices);
310          }
311          needs_reset = true;
312          submit++;
313          break;
314       case RD_GPU_ID:
315          if (!got_gpu_id) {
316             uint32_t gpu_id = parse_gpu_id(ps.buf);
317             if (!gpu_id)
318                break;
319             options.dev_id.gpu_id = gpu_id;
320             printl(2, "gpu_id: %d\n", options.dev_id.gpu_id);
321 
322             options.info = fd_dev_info_raw(&options.dev_id);
323             if (!options.info)
324                break;
325 
326             cffdec_init(&options);
327             got_gpu_id = 1;
328          }
329          break;
330       case RD_CHIP_ID:
331          if (!got_gpu_id) {
332             options.dev_id.chip_id = parse_chip_id(ps.buf);
333             printl(2, "chip_id: 0x%" PRIx64 "\n", options.dev_id.chip_id);
334 
335             options.info = fd_dev_info_raw(&options.dev_id);
336             if (!options.info)
337                break;
338 
339             cffdec_init(&options);
340             got_gpu_id = 1;
341          }
342          break;
343       default:
344          break;
345       }
346    }
347 
348    script_end_cmdstream();
349 
350    reset_buffers();
351 
352    io_close(io);
353    fflush(stdout);
354 
355    if (ps.ret < 0) {
356       printf("corrupt file\n");
357    }
358    return 0;
359 }
360