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