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 <sys/stat.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41
42 #include "buffers.h"
43 #include "cffdec.h"
44 #include "disasm.h"
45 #include "io.h"
46 #include "pager.h"
47 #include "redump.h"
48 #include "rnnutil.h"
49 #include "script.h"
50
51 static struct cffdec_options options = {
52 .gpu_id = 220,
53 };
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 void
parse_addr(uint32_t * buf,int sz,unsigned int * len,uint64_t * gpuaddr)245 parse_addr(uint32_t *buf, int sz, unsigned int *len, uint64_t *gpuaddr)
246 {
247 *gpuaddr = buf[0];
248 *len = buf[1];
249 if (sz > 8)
250 *gpuaddr |= ((uint64_t)(buf[2])) << 32;
251 }
252
253 static int
handle_file(const char * filename,int start,int end,int draw)254 handle_file(const char *filename, int start, int end, int draw)
255 {
256 enum rd_sect_type type = RD_NONE;
257 void *buf = NULL;
258 struct io *io;
259 int submit = 0, got_gpu_id = 0;
260 int sz, ret = 0;
261 bool needs_reset = false;
262 bool skip = false;
263
264 options.draw_filter = draw;
265
266 cffdec_init(&options);
267
268 if (!options.unit_test)
269 printf("Reading %s...\n", filename);
270
271 script_start_cmdstream(filename);
272
273 if (!strcmp(filename, "-"))
274 io = io_openfd(0);
275 else
276 io = io_open(filename);
277
278 if (!io) {
279 fprintf(stderr, "could not open: %s\n", filename);
280 return -1;
281 }
282
283 struct {
284 unsigned int len;
285 uint64_t gpuaddr;
286 } gpuaddr = {0};
287
288 while (true) {
289 uint32_t arr[2];
290
291 ret = io_readn(io, arr, 8);
292 if (ret <= 0)
293 goto end;
294
295 while ((arr[0] == 0xffffffff) && (arr[1] == 0xffffffff)) {
296 ret = io_readn(io, arr, 8);
297 if (ret <= 0)
298 goto end;
299 }
300
301 type = arr[0];
302 sz = arr[1];
303
304 if (sz < 0) {
305 ret = -1;
306 goto end;
307 }
308
309 free(buf);
310
311 needs_wfi = false;
312
313 buf = malloc(sz + 1);
314 ((char *)buf)[sz] = '\0';
315 ret = io_readn(io, buf, sz);
316 if (ret < 0)
317 goto end;
318
319 switch (type) {
320 case RD_TEST:
321 printl(1, "test: %s\n", (char *)buf);
322 break;
323 case RD_CMD:
324 is_blob = true;
325 printl(2, "cmd: %s\n", (char *)buf);
326 skip = false;
327 if (exename) {
328 skip |= (strstr(buf, exename) != buf);
329 } else if (!show_comp) {
330 skip |= (strstr(buf, "fdperf") == buf);
331 skip |= (strstr(buf, "chrome") == buf);
332 skip |= (strstr(buf, "surfaceflinger") == buf);
333 skip |= ((char *)buf)[0] == 'X';
334 }
335 break;
336 case RD_VERT_SHADER:
337 printl(2, "vertex shader:\n%s\n", (char *)buf);
338 break;
339 case RD_FRAG_SHADER:
340 printl(2, "fragment shader:\n%s\n", (char *)buf);
341 break;
342 case RD_GPUADDR:
343 if (needs_reset) {
344 reset_buffers();
345 needs_reset = false;
346 }
347 parse_addr(buf, sz, &gpuaddr.len, &gpuaddr.gpuaddr);
348 break;
349 case RD_BUFFER_CONTENTS:
350 add_buffer(gpuaddr.gpuaddr, gpuaddr.len, buf);
351 buf = NULL;
352 break;
353 case RD_CMDSTREAM_ADDR:
354 if ((start <= submit) && (submit <= end)) {
355 unsigned int sizedwords;
356 uint64_t gpuaddr;
357 parse_addr(buf, sz, &sizedwords, &gpuaddr);
358 printl(2, "############################################################\n");
359 printl(2, "cmdstream: %d dwords\n", sizedwords);
360 if (!skip) {
361 script_start_submit();
362 dump_commands(hostptr(gpuaddr), sizedwords, 0);
363 script_end_submit();
364 }
365 printl(2, "############################################################\n");
366 printl(2, "vertices: %d\n", vertices);
367 }
368 needs_reset = true;
369 submit++;
370 break;
371 case RD_GPU_ID:
372 if (!got_gpu_id) {
373 uint32_t gpu_id = *((unsigned int *)buf);
374 if (!gpu_id)
375 break;
376 options.gpu_id = gpu_id;
377 printl(2, "gpu_id: %d\n", options.gpu_id);
378 cffdec_init(&options);
379 got_gpu_id = 1;
380 }
381 break;
382 case RD_CHIP_ID:
383 if (!got_gpu_id) {
384 uint64_t chip_id = *((uint64_t *)buf);
385 options.gpu_id = 100 * ((chip_id >> 24) & 0xff) +
386 10 * ((chip_id >> 16) & 0xff) +
387 ((chip_id >> 8) & 0xff);
388 printl(2, "gpu_id: %d\n", options.gpu_id);
389 cffdec_init(&options);
390 got_gpu_id = 1;
391 }
392 break;
393 default:
394 break;
395 }
396 }
397
398 end:
399 script_end_cmdstream();
400
401 io_close(io);
402 fflush(stdout);
403
404 if (ret < 0) {
405 printf("corrupt file\n");
406 }
407 return 0;
408 }
409