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