• 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 <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