• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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