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