• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2020 Google, Inc.
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 /*
25  * Decoder for devcoredump traces from drm/msm.  In case of a gpu crash/hang,
26  * the coredump should be found in:
27  *
28  *    /sys/class/devcoredump/devcd<n>/data
29  *
30  * The crashdump will hang around for 5min, it can be cleared by writing to
31  * the file, ie:
32  *
33  *    echo 1 > /sys/class/devcoredump/devcd<n>/data
34  *
35  * (the driver won't log any new crashdumps until the previous one is cleared
36  * or times out after 5min)
37  */
38 
39 
40 #include "crashdec.h"
41 
42 static FILE *in;
43 bool verbose;
44 
45 struct rnn *rnn_gmu;
46 struct rnn *rnn_control;
47 struct rnn *rnn_pipe;
48 
49 struct cffdec_options options = {
50    .draw_filter = -1,
51 };
52 
53 /*
54  * Helpers to read register values:
55  */
56 
57 /* read registers that are 64b on 64b GPUs (ie. a5xx+) */
58 static uint64_t
regval64(const char * name)59 regval64(const char *name)
60 {
61    unsigned reg = regbase(name);
62    assert(reg);
63    uint64_t val = reg_val(reg);
64    if (is_64b())
65       val |= ((uint64_t)reg_val(reg + 1)) << 32;
66    return val;
67 }
68 
69 static uint32_t
regval(const char * name)70 regval(const char *name)
71 {
72    unsigned reg = regbase(name);
73    assert(reg);
74    return reg_val(reg);
75 }
76 
77 /*
78  * Line reading and string helpers:
79  */
80 
81 static char *
replacestr(char * line,const char * find,const char * replace)82 replacestr(char *line, const char *find, const char *replace)
83 {
84    char *tail, *s;
85 
86    if (!(s = strstr(line, find)))
87       return line;
88 
89    tail = s + strlen(find);
90 
91    char *newline;
92    asprintf(&newline, "%.*s%s%s", (int)(s - line), line, replace, tail);
93    free(line);
94 
95    return newline;
96 }
97 
98 static char *lastline;
99 static char *pushedline;
100 
101 static const char *
popline(void)102 popline(void)
103 {
104    char *r = pushedline;
105 
106    if (r) {
107       pushedline = NULL;
108       return r;
109    }
110 
111    free(lastline);
112 
113    size_t n = 0;
114    if (getline(&r, &n, in) < 0)
115       exit(0);
116 
117    /* Handle section name typo's from earlier kernels: */
118    r = replacestr(r, "CP_MEMPOOOL", "CP_MEMPOOL");
119    r = replacestr(r, "CP_SEQ_STAT", "CP_SQE_STAT");
120 
121    lastline = r;
122    return r;
123 }
124 
125 static void
pushline(void)126 pushline(void)
127 {
128    assert(!pushedline);
129    pushedline = lastline;
130 }
131 
132 static uint32_t *
popline_ascii85(uint32_t sizedwords)133 popline_ascii85(uint32_t sizedwords)
134 {
135    const char *line = popline();
136 
137    /* At this point we exepct the ascii85 data to be indented *some*
138     * amount, and to terminate at the end of the line.  So just eat
139     * up the leading whitespace.
140     */
141    assert(*line == ' ');
142    while (*line == ' ')
143       line++;
144 
145    uint32_t *buf = calloc(1, 4 * sizedwords);
146    int idx = 0;
147 
148    while (*line != '\n') {
149       if (*line == 'z') {
150          buf[idx++] = 0;
151          line++;
152          continue;
153       }
154 
155       uint32_t accum = 0;
156       for (int i = 0; (i < 5) && (*line != '\n'); i++) {
157          accum *= 85;
158          accum += *line - '!';
159          line++;
160       }
161 
162       buf[idx++] = accum;
163    }
164 
165    return buf;
166 }
167 
168 static bool
startswith(const char * line,const char * start)169 startswith(const char *line, const char *start)
170 {
171    return strstr(line, start) == line;
172 }
173 
174 static void
parseline(const char * line,const char * fmt,...)175 parseline(const char *line, const char *fmt, ...)
176 {
177    int fmtlen = strlen(fmt);
178    int n = 0;
179    int l = 0;
180 
181    /* scan fmt string to extract expected # of conversions: */
182    for (int i = 0; i < fmtlen; i++) {
183       if (fmt[i] == '%') {
184          if (i == (l - 1)) { /* prev char was %, ie. we have %% */
185             n--;
186             l = 0;
187          } else {
188             n++;
189             l = i;
190          }
191       }
192    }
193 
194    va_list ap;
195    va_start(ap, fmt);
196    if (vsscanf(line, fmt, ap) != n) {
197       fprintf(stderr, "parse error scanning: '%s'\n", fmt);
198       exit(1);
199    }
200    va_end(ap);
201 }
202 
203 #define foreach_line_in_section(_line)                                         \
204    for (const char *_line = popline(); _line; _line = popline())               \
205       /* check for start of next section */                                    \
206       if (_line[0] != ' ') {                                                   \
207          pushline();                                                           \
208          break;                                                                \
209       } else
210 
211 /*
212  * Decode ringbuffer section:
213  */
214 
215 static struct {
216    uint64_t iova;
217    uint32_t rptr;
218    uint32_t wptr;
219    uint32_t size;
220    uint32_t *buf;
221 } ringbuffers[5];
222 
223 static void
decode_ringbuffer(void)224 decode_ringbuffer(void)
225 {
226    int id = 0;
227 
228    foreach_line_in_section (line) {
229       if (startswith(line, "  - id:")) {
230          parseline(line, "  - id: %d", &id);
231          assert(id < ARRAY_SIZE(ringbuffers));
232       } else if (startswith(line, "    iova:")) {
233          parseline(line, "    iova: %" PRIx64, &ringbuffers[id].iova);
234       } else if (startswith(line, "    rptr:")) {
235          parseline(line, "    rptr: %d", &ringbuffers[id].rptr);
236       } else if (startswith(line, "    wptr:")) {
237          parseline(line, "    wptr: %d", &ringbuffers[id].wptr);
238       } else if (startswith(line, "    size:")) {
239          parseline(line, "    size: %d", &ringbuffers[id].size);
240       } else if (startswith(line, "    data: !!ascii85 |")) {
241          ringbuffers[id].buf = popline_ascii85(ringbuffers[id].size / 4);
242          add_buffer(ringbuffers[id].iova, ringbuffers[id].size,
243                     ringbuffers[id].buf);
244          continue;
245       }
246 
247       printf("%s", line);
248    }
249 }
250 
251 /*
252  * Decode GMU log
253  */
254 
255 static void
decode_gmu_log(void)256 decode_gmu_log(void)
257 {
258    uint64_t iova;
259    uint32_t size;
260 
261    foreach_line_in_section (line) {
262       if (startswith(line, "    iova:")) {
263          parseline(line, "    iova: %" PRIx64, &iova);
264       } else if (startswith(line, "    size:")) {
265          parseline(line, "    size: %u", &size);
266       } else if (startswith(line, "    data: !!ascii85 |")) {
267          void *buf = popline_ascii85(size / 4);
268 
269          dump_hex_ascii(buf, size, 1);
270 
271          free(buf);
272 
273          continue;
274       }
275 
276       printf("%s", line);
277    }
278 }
279 
280 /*
281  * Decode HFI queues
282  */
283 
284 static void
decode_gmu_hfi(void)285 decode_gmu_hfi(void)
286 {
287    struct a6xx_hfi_state hfi = {};
288 
289    /* Initialize the history buffers with invalid entries (-1): */
290    memset(&hfi.history, 0xff, sizeof(hfi.history));
291 
292    foreach_line_in_section (line) {
293       if (startswith(line, "    iova:")) {
294          parseline(line, "    iova: %" PRIx64, &hfi.iova);
295       } else if (startswith(line, "    size:")) {
296          parseline(line, "    size: %u", &hfi.size);
297       } else if (startswith(line, "    queue-history")) {
298          unsigned qidx, dummy;
299 
300          parseline(line, "    queue-history[%u]:", &qidx);
301          assert(qidx < ARRAY_SIZE(hfi.history));
302 
303          parseline(line, "    queue-history[%u]: %d %d %d %d %d %d %d %d", &dummy,
304                    &hfi.history[qidx][0], &hfi.history[qidx][1],
305                    &hfi.history[qidx][2], &hfi.history[qidx][3],
306                    &hfi.history[qidx][4], &hfi.history[qidx][5],
307                    &hfi.history[qidx][6], &hfi.history[qidx][7]);
308       } else if (startswith(line, "    data: !!ascii85 |")) {
309          hfi.buf = popline_ascii85(hfi.size / 4);
310 
311          if (verbose)
312             dump_hex_ascii(hfi.buf, hfi.size, 1);
313 
314          dump_gmu_hfi(&hfi);
315 
316          free(hfi.buf);
317 
318          continue;
319       }
320 
321       printf("%s", line);
322    }
323 }
324 
325 static bool
valid_header(uint32_t pkt)326 valid_header(uint32_t pkt)
327 {
328    if (options.gpu_id >= 500) {
329       return pkt_is_type4(pkt) || pkt_is_type7(pkt);
330    } else {
331       /* TODO maybe we can check validish looking pkt3 opc or pkt0
332        * register offset.. the cmds sent by kernel are usually
333        * fairly limited (other than initialization) which confines
334        * the search space a bit..
335        */
336       return true;
337    }
338 }
339 
340 static void
dump_cmdstream(void)341 dump_cmdstream(void)
342 {
343    uint64_t rb_base = regval64("CP_RB_BASE");
344 
345    printf("got rb_base=%" PRIx64 "\n", rb_base);
346 
347    options.ibs[1].base = regval64("CP_IB1_BASE");
348    options.ibs[1].rem = regval("CP_IB1_REM_SIZE");
349    options.ibs[2].base = regval64("CP_IB2_BASE");
350    options.ibs[2].rem = regval("CP_IB2_REM_SIZE");
351 
352    /* Adjust remaining size to account for cmdstream slurped into ROQ
353     * but not yet consumed by SQE
354     *
355     * TODO add support for earlier GPUs once we tease out the needed
356     * registers.. see crashit.c in msmtest for hints.
357     *
358     * TODO it would be nice to be able to extract out register bitfields
359     * by name rather than hard-coding this.
360     */
361    if (is_a6xx()) {
362       options.ibs[1].rem += regval("CP_CSQ_IB1_STAT") >> 16;
363       options.ibs[2].rem += regval("CP_CSQ_IB2_STAT") >> 16;
364    }
365 
366    printf("IB1: %" PRIx64 ", %u\n", options.ibs[1].base, options.ibs[1].rem);
367    printf("IB2: %" PRIx64 ", %u\n", options.ibs[2].base, options.ibs[2].rem);
368 
369    /* now that we've got the regvals we want, reset register state
370     * so we aren't seeing values from decode_registers();
371     */
372    reset_regs();
373 
374    for (int id = 0; id < ARRAY_SIZE(ringbuffers); id++) {
375       if (ringbuffers[id].iova != rb_base)
376          continue;
377       if (!ringbuffers[id].size)
378          continue;
379 
380       printf("found ring!\n");
381 
382       /* The kernel level ringbuffer (RB) wraps around, which
383        * cffdec doesn't really deal with.. so figure out how
384        * many dwords are unread
385        */
386       unsigned ringszdw = ringbuffers[id].size >> 2; /* in dwords */
387 
388       if (verbose) {
389          dump_commands(ringbuffers[id].buf, ringszdw, 0);
390          return;
391       }
392 
393 /* helper macro to deal with modulo size math: */
394 #define mod_add(b, v) ((ringszdw + (int)(b) + (int)(v)) % ringszdw)
395 
396       /* The rptr will (most likely) have moved past the IB to
397        * userspace cmdstream, so back up a bit, and then advance
398        * until we find a valid start of a packet.. this is going
399        * to be less reliable on a4xx and before (pkt0/pkt3),
400        * compared to pkt4/pkt7 with parity bits
401        */
402       const int lookback = 12;
403       unsigned rptr = mod_add(ringbuffers[id].rptr, -lookback);
404 
405       for (int idx = 0; idx < lookback; idx++) {
406          if (valid_header(ringbuffers[id].buf[rptr]))
407             break;
408          rptr = mod_add(rptr, 1);
409       }
410 
411       unsigned cmdszdw = mod_add(ringbuffers[id].wptr, -rptr);
412 
413       printf("got cmdszdw=%d\n", cmdszdw);
414       uint32_t *buf = malloc(cmdszdw * 4);
415 
416       for (int idx = 0; idx < cmdszdw; idx++) {
417          int p = mod_add(rptr, idx);
418          buf[idx] = ringbuffers[id].buf[p];
419       }
420 
421       dump_commands(buf, cmdszdw, 0);
422       free(buf);
423    }
424 }
425 
426 /*
427  * Decode 'bos' (buffers) section:
428  */
429 
430 static void
decode_bos(void)431 decode_bos(void)
432 {
433    uint32_t size = 0;
434    uint64_t iova = 0;
435 
436    foreach_line_in_section (line) {
437       if (startswith(line, "  - iova:")) {
438          parseline(line, "  - iova: %" PRIx64, &iova);
439       } else if (startswith(line, "    size:")) {
440          parseline(line, "    size: %u", &size);
441       } else if (startswith(line, "    data: !!ascii85 |")) {
442          uint32_t *buf = popline_ascii85(size / 4);
443 
444          if (verbose)
445             dump_hex_ascii(buf, size, 1);
446 
447          add_buffer(iova, size, buf);
448 
449          continue;
450       }
451 
452       printf("%s", line);
453    }
454 }
455 
456 /*
457  * Decode registers section:
458  */
459 
460 void
dump_register(struct rnn * rnn,uint32_t offset,uint32_t value)461 dump_register(struct rnn *rnn, uint32_t offset, uint32_t value)
462 {
463    struct rnndecaddrinfo *info = rnn_reginfo(rnn, offset);
464    if (info && info->typeinfo) {
465       char *decoded = rnndec_decodeval(rnn->vc, info->typeinfo, value);
466       printf("%s: %s\n", info->name, decoded);
467    } else if (info) {
468       printf("%s: %08x\n", info->name, value);
469    } else {
470       printf("<%04x>: %08x\n", offset, value);
471    }
472 }
473 
474 static void
decode_gmu_registers(void)475 decode_gmu_registers(void)
476 {
477    foreach_line_in_section (line) {
478       uint32_t offset, value;
479       parseline(line, "  - { offset: %x, value: %x }", &offset, &value);
480 
481       printf("\t%08x\t", value);
482       dump_register(rnn_gmu, offset / 4, value);
483    }
484 }
485 
486 static void
decode_registers(void)487 decode_registers(void)
488 {
489    foreach_line_in_section (line) {
490       uint32_t offset, value;
491       parseline(line, "  - { offset: %x, value: %x }", &offset, &value);
492 
493       reg_set(offset / 4, value);
494       printf("\t%08x", value);
495       dump_register_val(offset / 4, value, 0);
496    }
497 }
498 
499 /* similar to registers section, but for banked context regs: */
500 static void
decode_clusters(void)501 decode_clusters(void)
502 {
503    foreach_line_in_section (line) {
504       if (startswith(line, "  - cluster-name:") ||
505           startswith(line, "    - context:")) {
506          printf("%s", line);
507          continue;
508       }
509 
510       uint32_t offset, value;
511       parseline(line, "      - { offset: %x, value: %x }", &offset, &value);
512 
513       printf("\t%08x", value);
514       dump_register_val(offset / 4, value, 0);
515    }
516 }
517 
518 /*
519  * Decode indexed-registers.. these aren't like normal registers, but a
520  * sort of FIFO where successive reads pop out associated debug state.
521  */
522 
523 static void
dump_cp_sqe_stat(uint32_t * stat)524 dump_cp_sqe_stat(uint32_t *stat)
525 {
526    printf("\t PC: %04x\n", stat[0]);
527    stat++;
528 
529    if (is_a6xx() && valid_header(stat[0])) {
530       if (pkt_is_type7(stat[0])) {
531          unsigned opc = cp_type7_opcode(stat[0]);
532          const char *name = pktname(opc);
533          if (name)
534             printf("\tPKT: %s\n", name);
535       } else {
536          /* Not sure if this case can happen: */
537       }
538    }
539 
540    for (int i = 0; i < 16; i++) {
541       printf("\t$%02x: %08x\t\t$%02x: %08x\n", i + 1, stat[i], i + 16 + 1,
542              stat[i + 16]);
543    }
544 }
545 
546 static void
dump_control_regs(uint32_t * regs)547 dump_control_regs(uint32_t *regs)
548 {
549    if (!rnn_control)
550       return;
551 
552    /* Control regs 0x100-0x17f are a scratch space to be used by the
553     * firmware however it wants, unlike lower regs which involve some
554     * fixed-function units. Therefore only these registers get dumped
555     * directly.
556     */
557    for (uint32_t i = 0; i < 0x80; i++) {
558       printf("\t%08x\t", regs[i]);
559       dump_register(rnn_control, i + 0x100, regs[i]);
560    }
561 }
562 
563 static void
dump_cp_ucode_dbg(uint32_t * dbg)564 dump_cp_ucode_dbg(uint32_t *dbg)
565 {
566    /* Notes on the data:
567     * There seems to be a section every 4096 DWORD's. The sections aren't
568     * all the same size, so the rest of the 4096 DWORD's are filled with
569     * mirrors of the actual data.
570     */
571 
572    for (int section = 0; section < 6; section++, dbg += 0x1000) {
573       switch (section) {
574       case 0:
575          /* Contains scattered data from a630_sqe.fw: */
576          printf("\tSQE instruction cache:\n");
577          dump_hex_ascii(dbg, 4 * 0x400, 1);
578          break;
579       case 1:
580          printf("\tUnknown 1:\n");
581          dump_hex_ascii(dbg, 4 * 0x80, 1);
582          break;
583       case 2:
584          printf("\tUnknown 2:\n");
585          dump_hex_ascii(dbg, 4 * 0x200, 1);
586          break;
587       case 3:
588          printf("\tUnknown 3:\n");
589          dump_hex_ascii(dbg, 4 * 0x80, 1);
590          break;
591       case 4:
592          /* Don't bother printing this normally */
593          if (verbose) {
594             printf("\tSQE packet jumptable contents:\n");
595             dump_hex_ascii(dbg, 4 * 0x80, 1);
596          }
597          break;
598       case 5:
599          printf("\tSQE scratch control regs:\n");
600          dump_control_regs(dbg);
601          break;
602       }
603    }
604 }
605 
606 static void
decode_indexed_registers(void)607 decode_indexed_registers(void)
608 {
609    char *name = NULL;
610    uint32_t sizedwords = 0;
611 
612    foreach_line_in_section (line) {
613       if (startswith(line, "  - regs-name:")) {
614          free(name);
615          parseline(line, "  - regs-name: %ms", &name);
616       } else if (startswith(line, "    dwords:")) {
617          parseline(line, "    dwords: %u", &sizedwords);
618       } else if (startswith(line, "    data: !!ascii85 |")) {
619          uint32_t *buf = popline_ascii85(sizedwords);
620 
621          /* some of the sections are pretty large, and are (at least
622           * so far) not useful, so skip them if not in verbose mode:
623           */
624          bool dump = verbose || !strcmp(name, "CP_SQE_STAT") ||
625                      !strcmp(name, "CP_DRAW_STATE") ||
626                      !strcmp(name, "CP_ROQ") || 0;
627 
628          if (!strcmp(name, "CP_SQE_STAT"))
629             dump_cp_sqe_stat(buf);
630 
631          if (!strcmp(name, "CP_UCODE_DBG_DATA"))
632             dump_cp_ucode_dbg(buf);
633 
634          if (!strcmp(name, "CP_MEMPOOL"))
635             dump_cp_mem_pool(buf);
636 
637          if (dump)
638             dump_hex_ascii(buf, 4 * sizedwords, 1);
639 
640          free(buf);
641 
642          continue;
643       }
644 
645       printf("%s", line);
646    }
647 }
648 
649 /*
650  * Decode shader-blocks:
651  */
652 
653 static void
decode_shader_blocks(void)654 decode_shader_blocks(void)
655 {
656    char *type = NULL;
657    uint32_t sizedwords = 0;
658 
659    foreach_line_in_section (line) {
660       if (startswith(line, "  - type:")) {
661          free(type);
662          parseline(line, "  - type: %ms", &type);
663       } else if (startswith(line, "      size:")) {
664          parseline(line, "      size: %u", &sizedwords);
665       } else if (startswith(line, "    data: !!ascii85 |")) {
666          uint32_t *buf = popline_ascii85(sizedwords);
667 
668          /* some of the sections are pretty large, and are (at least
669           * so far) not useful, so skip them if not in verbose mode:
670           */
671          bool dump = verbose || !strcmp(type, "A6XX_SP_INST_DATA") ||
672                      !strcmp(type, "A6XX_HLSQ_INST_RAM") || 0;
673 
674          if (!strcmp(type, "A6XX_SP_INST_DATA") ||
675              !strcmp(type, "A6XX_HLSQ_INST_RAM")) {
676             /* TODO this section actually contains multiple shaders
677              * (or parts of shaders?), so perhaps we should search
678              * for ends of shaders and decode each?
679              */
680             try_disasm_a3xx(buf, sizedwords, 1, stdout, options.gpu_id);
681          }
682 
683          if (dump)
684             dump_hex_ascii(buf, 4 * sizedwords, 1);
685 
686          free(buf);
687 
688          continue;
689       }
690 
691       printf("%s", line);
692    }
693 
694    free(type);
695 }
696 
697 /*
698  * Decode debugbus section:
699  */
700 
701 static void
decode_debugbus(void)702 decode_debugbus(void)
703 {
704    char *block = NULL;
705    uint32_t sizedwords = 0;
706 
707    foreach_line_in_section (line) {
708       if (startswith(line, "  - debugbus-block:")) {
709          free(block);
710          parseline(line, "  - debugbus-block: %ms", &block);
711       } else if (startswith(line, "    count:")) {
712          parseline(line, "    count: %u", &sizedwords);
713       } else if (startswith(line, "    data: !!ascii85 |")) {
714          uint32_t *buf = popline_ascii85(sizedwords);
715 
716          /* some of the sections are pretty large, and are (at least
717           * so far) not useful, so skip them if not in verbose mode:
718           */
719          bool dump = verbose || 0;
720 
721          if (dump)
722             dump_hex_ascii(buf, 4 * sizedwords, 1);
723 
724          free(buf);
725 
726          continue;
727       }
728 
729       printf("%s", line);
730    }
731 }
732 
733 /*
734  * Main crashdump decode loop:
735  */
736 
737 static void
decode(void)738 decode(void)
739 {
740    const char *line;
741 
742    while ((line = popline())) {
743       printf("%s", line);
744       if (startswith(line, "revision:")) {
745          unsigned core, major, minor, patchid;
746 
747          parseline(line, "revision: %u (%u.%u.%u.%u)", &options.gpu_id,
748                    &core, &major, &minor, &patchid);
749 
750          if (options.gpu_id == 0) {
751             options.gpu_id = (core * 100) + (major * 10) + minor;
752          }
753 
754          printf("Got gpu_id=%u\n", options.gpu_id);
755 
756          cffdec_init(&options);
757 
758          if (is_a6xx()) {
759             rnn_gmu = rnn_new(!options.color);
760             rnn_load_file(rnn_gmu, "adreno/a6xx_gmu.xml", "A6XX");
761             rnn_control = rnn_new(!options.color);
762             rnn_load_file(rnn_control, "adreno/adreno_control_regs.xml",
763                           "A6XX_CONTROL_REG");
764             rnn_pipe = rnn_new(!options.color);
765             rnn_load_file(rnn_pipe, "adreno/adreno_pipe_regs.xml",
766                           "A6XX_PIPE_REG");
767          } else if (is_a5xx()) {
768             rnn_control = rnn_new(!options.color);
769             rnn_load_file(rnn_control, "adreno/adreno_control_regs.xml",
770                           "A5XX_CONTROL_REG");
771          } else {
772             rnn_control = NULL;
773          }
774       } else if (startswith(line, "bos:")) {
775          decode_bos();
776       } else if (startswith(line, "ringbuffer:")) {
777          decode_ringbuffer();
778       } else if (startswith(line, "gmu-log:")) {
779          decode_gmu_log();
780       } else if (startswith(line, "gmu-hfi:")) {
781          decode_gmu_hfi();
782       } else if (startswith(line, "registers:")) {
783          decode_registers();
784 
785          /* after we've recorded buffer contents, and CP register values,
786           * we can take a stab at decoding the cmdstream:
787           */
788          dump_cmdstream();
789       } else if (startswith(line, "registers-gmu:")) {
790          decode_gmu_registers();
791       } else if (startswith(line, "indexed-registers:")) {
792          decode_indexed_registers();
793       } else if (startswith(line, "shader-blocks:")) {
794          decode_shader_blocks();
795       } else if (startswith(line, "clusters:")) {
796          decode_clusters();
797       } else if (startswith(line, "debugbus:")) {
798          decode_debugbus();
799       }
800    }
801 }
802 
803 /*
804  * Usage and argument parsing:
805  */
806 
807 static void
usage(void)808 usage(void)
809 {
810    /* clang-format off */
811    fprintf(stderr, "Usage:\n\n"
812            "\tcrashdec [-achmsv] [-f FILE]\n\n"
813            "Options:\n"
814            "\t-a, --allregs   - show all registers (including ones not written since\n"
815            "\t                  previous draw) at each draw\n"
816            "\t-c, --color     - use colors\n"
817            "\t-f, --file=FILE - read input from specified file (rather than stdin)\n"
818            "\t-h, --help      - this usage message\n"
819            "\t-m, --markers   - try to decode CP_NOP string markers\n"
820            "\t-s, --summary   - don't show individual register writes, but just show\n"
821            "\t                  register values on draws\n"
822            "\t-v, --verbose   - dump more verbose output, including contents of\n"
823            "\t                  less interesting buffers\n"
824            "\n"
825    );
826    /* clang-format on */
827    exit(2);
828 }
829 
830 /* clang-format off */
831 static const struct option opts[] = {
832       { .name = "allregs", .has_arg = 0, NULL, 'a' },
833       { .name = "color",   .has_arg = 0, NULL, 'c' },
834       { .name = "file",    .has_arg = 1, NULL, 'f' },
835       { .name = "help",    .has_arg = 0, NULL, 'h' },
836       { .name = "markers", .has_arg = 0, NULL, 'm' },
837       { .name = "summary", .has_arg = 0, NULL, 's' },
838       { .name = "verbose", .has_arg = 0, NULL, 'v' },
839       {}
840 };
841 /* clang-format on */
842 
843 static bool interactive;
844 
845 static void
cleanup(void)846 cleanup(void)
847 {
848    fflush(stdout);
849 
850    if (interactive) {
851       pager_close();
852    }
853 }
854 
855 int
main(int argc,char ** argv)856 main(int argc, char **argv)
857 {
858    int c;
859 
860    interactive = isatty(STDOUT_FILENO);
861    options.color = interactive;
862 
863    /* default to read from stdin: */
864    in = stdin;
865 
866    while ((c = getopt_long(argc, argv, "acf:hmsv", opts, NULL)) != -1) {
867       switch (c) {
868       case 'a':
869          options.allregs = true;
870          break;
871       case 'c':
872          options.color = true;
873          break;
874       case 'f':
875          in = fopen(optarg, "r");
876          break;
877       case 'm':
878          options.decode_markers = true;
879          break;
880       case 's':
881          options.summary = true;
882          break;
883       case 'v':
884          verbose = true;
885          break;
886       case 'h':
887       default:
888          usage();
889       }
890    }
891 
892    disasm_a3xx_set_debug(PRINT_RAW);
893 
894    if (interactive) {
895       pager_open();
896    }
897 
898    atexit(cleanup);
899 
900    decode();
901    cleanup();
902 }
903