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