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