Path: blob/21.2-virgl/src/freedreno/decode/pgmdump2.c
4565 views
/*1* Copyright (c) 2018 Rob Clark <[email protected]>2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,19* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE20* SOFTWARE.21*/2223/*24* Decoder for "new" GL_OES_get_program_binary format.25*26* Overall structure is:27*28* - header at top, contains, amongst other things, offsets of29* per shader stage sections.30* - per shader stage section (shader_info) starts with a header,31* followed by a variably length list of descriptors. Each32* descriptor has a type/count/size plus offset from the start33* of shader_info section where the data is found34*/3536#include <assert.h>37#include <ctype.h>38#include <fcntl.h>39#include <stddef.h>40#include <stdint.h>41#include <stdio.h>42#include <stdlib.h>43#include <string.h>44#include <unistd.h>45#include <sys/stat.h>46#include <sys/types.h>4748#include "disasm.h"49#include "io.h"50#include "redump.h"51#include "util.h"5253const char *infile;54static int dump_full = 0;55static int dump_offsets = 0;56static int gpu_id = 320;57static int shaderdb = 0; /* output shaderdb style traces to stderr */5859struct state {60char *buf;61int sz;62int lvl;6364/* current shader_info section, some offsets calculated relative to65* this, rather than relative to start of buffer.66*/67void *shader;6869/* size of each entry within a shader_descriptor_blk: */70int desc_size;7172const char *shader_type;73int full_regs;74int half_regs;75};7677#define PACKED __attribute__((__packed__))7879#define OFF(field) \80do { \81if (dump_offsets) \82printf("%08x: ", (uint32_t)((char *)&field - state->buf)); \83} while (0)8485/* decode field as hex */86#define X(s, field) \87do { \88OFF(s->field); \89printf("%s%12s:\t0x%x\n", tab(state->lvl), #field, s->field); \90} while (0)9192/* decode field as digit */93#define D(s, field) \94do { \95OFF(s->field); \96printf("%s%12s:\t%u\n", tab(state->lvl), #field, s->field); \97} while (0)9899/* decode field as float/hex */100#define F(s, field) \101do { \102OFF(s->field); \103printf("%s%12s:\t%f (0x%0x)\n", tab(state->lvl), #field, d2f(s->field), \104s->field); \105} while (0)106107/* decode field as register: (type is 'r' or 'c') */108#define R(s, field, type) \109do { \110OFF(s->field); \111printf("%s%12s:\t%c%u.%c\n", tab(state->lvl), #field, type, \112(s->field >> 2), "xyzw"[s->field & 0x3]); \113} while (0)114115/* decode inline string (presumably null terminated?) */116#define S(s, field) \117do { \118OFF(s->field); \119printf("%s%12s:\t%s\n", tab(state->lvl), #field, s->field); \120} while (0)121122/* decode string-table string */123#define T(s, field) TODO124125/* decode field as unknown */126#define U(s, start, end) \127dump_unknown(state, s->unk_##start##_##end, 0x##start, \128(4 + 0x##end - 0x##start) / 4)129130/* decode field as offset to other section */131#define O(s, field, type) \132do { \133X(s, field); \134assert(s->field < state->sz); \135void *_p = &state->buf[s->field]; \136state->lvl++; \137decode_##type(state, _p); \138state->lvl--; \139} while (0)140141struct shader_info;142static void decode_shader_info(struct state *state, struct shader_info *info);143144static void145dump_unknown(struct state *state, void *buf, unsigned start, unsigned n)146{147uint32_t *ptr = buf;148uint8_t *ascii = buf;149150for (unsigned i = 0; i < n; i++) {151uint32_t d = ptr[i];152153if (dump_offsets)154printf("%08x:", (uint32_t)((char *)&ptr[i] - state->buf));155156printf("%s %04x:\t%08x", tab(state->lvl), start + i * 4, d);157158printf("\t|");159for (unsigned j = 0; j < 4; j++) {160uint8_t c = *(ascii++);161printf("%c", (isascii(c) && !iscntrl(c)) ? c : '.');162}163printf("|\t%f", d2f(d));164165/* TODO maybe scan for first non-null and non-ascii char starting from166* end of shader binary to (roughly) establish the start of the string167* table.. that would be a bit better filter for deciding if something168* might be a pointer into the string table. Also, the previous char169* to what it points to should probably be null.170*/171if ((d < state->sz) && isascii(state->buf[d]) &&172(strlen(&state->buf[d]) > 2) && isascii(state->buf[d + 1]))173printf("\t<== %s", &state->buf[d]);174175printf("\n");176}177}178179struct PACKED header {180uint32_t version; /* I guess, always b10bcace ? */181uint32_t unk_0004_0014[5];182uint32_t size;183uint32_t size2; /* just to be sure? */184uint32_t unk_0020_0020[1];185uint32_t186chksum; /* I guess? Small changes seem to result in big diffs here */187uint32_t unk_0028_0050[11];188uint32_t fs_info; /* offset of FS shader_info section */189uint32_t unk_0058_0090[15];190uint32_t vs_info; /* offset of VS shader_info section */191uint32_t unk_0098_00b0[7];192uint32_t vs_info2; /* offset of VS shader_info section (again?) */193uint32_t unk_00b8_0110[23];194uint32_t bs_info; /* offset of binning shader_info section */195};196197static void198decode_header(struct state *state, struct header *hdr)199{200X(hdr, version);201U(hdr, 0004, 0014);202X(hdr, size);203X(hdr, size2);204U(hdr, 0020, 0020);205X(hdr, chksum);206U(hdr, 0028, 0050);207state->shader_type = "FRAG";208O(hdr, fs_info, shader_info);209U(hdr, 0058, 0090);210state->shader_type = "VERT";211O(hdr, vs_info, shader_info);212U(hdr, 0098, 00b0);213assert(hdr->vs_info ==214hdr->vs_info2); /* not sure what this if it is ever different */215X(hdr, vs_info2);216U(hdr, 00b8, 0110);217state->shader_type = "BVERT";218O(hdr, bs_info, shader_info);219220/* not sure how much of the rest of contents before start of fs_info221* is the header, vs other things.. just dump it all as unknown for222* now:223*/224dump_unknown(state, (void *)hdr + sizeof(*hdr), sizeof(*hdr),225(hdr->fs_info - sizeof(*hdr)) / 4);226}227228struct PACKED shader_entry_point {229/* entry point name, ie. "main" of TBD length, followed by unknown */230char name[8];231};232233static void234decode_shader_entry_point(struct state *state, struct shader_entry_point *e)235{236S(e, name);237}238239struct PACKED shader_config {240uint32_t unk_0000_0008[3];241uint32_t full_regs;242uint32_t half_regs;243};244245static void246decode_shader_config(struct state *state, struct shader_config *cfg)247{248U(cfg, 0000, 0008);249D(cfg, full_regs);250D(cfg, half_regs);251252state->full_regs = cfg->full_regs;253state->half_regs = cfg->half_regs;254255/* dump reset of unknown (size differs btwn versions) */256dump_unknown(state, (void *)cfg + sizeof(*cfg), sizeof(*cfg),257(state->desc_size - sizeof(*cfg)) / 4);258}259260struct PACKED shader_io_block {261/* name of TBD length followed by unknown.. 42 dwords total */262char name[20];263uint32_t unk_0014_00a4[37];264};265266static void267decode_shader_io_block(struct state *state, struct shader_io_block *io)268{269S(io, name);270U(io, 0014, 00a4);271}272273struct PACKED shader_constant_block {274uint32_t value;275uint32_t unk_0004_000c[3];276uint32_t regid;277uint32_t unk_0014_0024[5];278};279280static void281decode_shader_constant_block(struct state *state,282struct shader_constant_block *c)283{284F(c, value);285U(c, 0004, 000c);286R(c, regid, 'c');287U(c, 0014, 0024);288}289290enum {291ENTRY_POINT = 0, /* shader_entry_point */292SHADER_CONFIG = 1, /* XXX placeholder name */293SHADER_INPUT = 2, /* shader_io_block */294SHADER_OUTPUT = 3, /* shader_io_block */295CONSTANTS = 6, /* shader_constant_block */296INTERNAL = 8, /* internal input, like bary.f coord */297SHADER = 10,298} shader_info_block_type;299300/* Refers to location of some type of records, with an offset relative to301* start of shader_info block.302*/303struct PACKED shader_descriptor_block {304uint32_t type; /* block type */305uint32_t offset; /* offset (relative to start of shader_info block) */306uint32_t size; /* size in bytes */307uint32_t count; /* number of records */308uint32_t unk_0010_0010[1];309};310311static void312decode_shader_descriptor_block(struct state *state,313struct shader_descriptor_block *blk)314{315D(blk, type);316X(blk, offset);317D(blk, size);318D(blk, count);319U(blk, 0010, 0010);320321/* offset relative to current shader block: */322void *ptr = state->shader + blk->offset;323324if (blk->count == 0) {325assert(blk->size == 0);326} else {327assert((blk->size % blk->count) == 0);328}329330state->desc_size = blk->size / blk->count;331state->lvl++;332for (unsigned i = 0; i < blk->count; i++) {333switch (blk->type) {334case ENTRY_POINT:335printf("%sentry point %u:\n", tab(state->lvl - 1), i);336decode_shader_entry_point(state, ptr);337break;338case SHADER_CONFIG:339printf("%sconfig %u:\n", tab(state->lvl - 1), i);340decode_shader_config(state, ptr);341break;342case SHADER_INPUT:343printf("%sinput %u:\n", tab(state->lvl - 1), i);344decode_shader_io_block(state, ptr);345break;346case SHADER_OUTPUT:347printf("%soutput %u:\n", tab(state->lvl - 1), i);348decode_shader_io_block(state, ptr);349break;350case INTERNAL:351printf("%sinternal input %u:\n", tab(state->lvl - 1), i);352decode_shader_io_block(state, ptr);353break;354case CONSTANTS:355printf("%sconstant %u:\n", tab(state->lvl - 1), i);356decode_shader_constant_block(state, ptr);357break;358case SHADER: {359struct shader_stats stats;360printf("%sshader %u:\n", tab(state->lvl - 1), i);361disasm_a3xx_stat(ptr, blk->size / 4, state->lvl, stdout, gpu_id,362&stats);363if (shaderdb) {364unsigned dwords = 2 * stats.instlen;365366if (gpu_id >= 400) {367dwords = ALIGN(dwords, 16 * 2);368} else {369dwords = ALIGN(dwords, 4 * 2);370}371372unsigned half_regs = state->half_regs;373unsigned full_regs = state->full_regs;374375/* On a6xx w/ merged/conflicting half and full regs, the376* full_regs footprint will be max of full_regs and half377* of half_regs.. we only care about which value is higher.378*/379if (gpu_id >= 600) {380/* footprint of half_regs in units of full_regs: */381unsigned half_full = (half_regs + 1) / 2;382if (half_full > full_regs)383full_regs = half_full;384half_regs = 0;385}386387fprintf(stderr,388"%s shader: %u inst, %u nops, %u non-nops, %u dwords, "389"%u half, %u full, %u constlen, "390"%u (ss), %u (sy), %d max_sun, %d loops\n",391state->shader_type, stats.instructions, stats.nops,392stats.instructions - stats.nops, dwords, half_regs,393full_regs, stats.constlen, stats.ss, stats.sy, 0,3940); /* max_sun or loops not possible */395}396/* this is a special case in a way, blk->count is # of397* instructions but disasm_a3xx() decodes all instructions,398* so just bail.399*/400i = blk->count;401break;402}403default:404dump_unknown(state, ptr, 0, state->desc_size / 4);405break;406}407ptr += state->desc_size;408}409state->lvl--;410}411412/* there looks like one of these per shader, followed by "main" and413* some more info, and then the shader itself.414*/415struct PACKED shader_info {416uint32_t unk_0000_0010[5];417uint32_t desc_off; /* offset to first descriptor block */418uint32_t num_blocks;419};420421static void422decode_shader_info(struct state *state, struct shader_info *info)423{424assert((info->desc_off % 4) == 0);425426U(info, 0000, 0010);427X(info, desc_off);428D(info, num_blocks);429430dump_unknown(state, &info[1], 0, (info->desc_off - sizeof(*info)) / 4);431432state->shader = info;433434struct shader_descriptor_block *blocks = ((void *)info) + info->desc_off;435for (unsigned i = 0; i < info->num_blocks; i++) {436printf("%sdescriptor %u:\n", tab(state->lvl), i);437state->lvl++;438decode_shader_descriptor_block(state, &blocks[i]);439state->lvl--;440}441}442443static void444dump_program(struct state *state)445{446struct header *hdr = (void *)state->buf;447448if (dump_full)449dump_unknown(state, state->buf, 0, state->sz / 4);450451decode_header(state, hdr);452}453454int455main(int argc, char **argv)456{457enum rd_sect_type type = RD_NONE;458enum debug_t debug = PRINT_RAW | PRINT_STATS;459void *buf = NULL;460int sz;461struct io *io;462int raw_program = 0;463464/* lame argument parsing: */465466while (1) {467if ((argc > 1) && !strcmp(argv[1], "--verbose")) {468debug |= PRINT_RAW | PRINT_VERBOSE;469argv++;470argc--;471continue;472}473if ((argc > 1) && !strcmp(argv[1], "--expand")) {474debug |= EXPAND_REPEAT;475argv++;476argc--;477continue;478}479if ((argc > 1) && !strcmp(argv[1], "--full")) {480/* only short dump, original shader, symbol table, and disassembly */481dump_full = 1;482argv++;483argc--;484continue;485}486if ((argc > 1) && !strcmp(argv[1], "--dump-offsets")) {487dump_offsets = 1;488argv++;489argc--;490continue;491}492if ((argc > 1) && !strcmp(argv[1], "--raw")) {493raw_program = 1;494argv++;495argc--;496continue;497}498if ((argc > 1) && !strcmp(argv[1], "--shaderdb")) {499shaderdb = 1;500argv++;501argc--;502continue;503}504break;505}506507if (argc != 2) {508fprintf(stderr, "usage: pgmdump2 [--verbose] [--expand] [--full] "509"[--dump-offsets] [--raw] [--shaderdb] testlog.rd\n");510return -1;511}512513disasm_a3xx_set_debug(debug);514515infile = argv[1];516517io = io_open(infile);518if (!io) {519fprintf(stderr, "could not open: %s\n", infile);520return -1;521}522523if (raw_program) {524io_readn(io, &sz, 4);525free(buf);526527/* note: allow hex dumps to go a bit past the end of the buffer..528* might see some garbage, but better than missing the last few bytes..529*/530buf = calloc(1, sz + 3);531io_readn(io, buf + 4, sz);532(*(int *)buf) = sz;533534struct state state = {535.buf = buf,536.sz = sz,537};538printf("############################################################\n");539printf("program:\n");540dump_program(&state);541printf("############################################################\n");542return 0;543}544545/* figure out what sort of input we are dealing with: */546if (!(check_extension(infile, ".rd") || check_extension(infile, ".rd.gz"))) {547int ret;548buf = calloc(1, 100 * 1024);549ret = io_readn(io, buf, 100 * 1024);550if (ret < 0) {551fprintf(stderr, "error: %m");552return -1;553}554return disasm_a3xx(buf, ret / 4, 0, stdout, gpu_id);555}556557while ((io_readn(io, &type, sizeof(type)) > 0) &&558(io_readn(io, &sz, 4) > 0)) {559free(buf);560561/* note: allow hex dumps to go a bit past the end of the buffer..562* might see some garbage, but better than missing the last few bytes..563*/564buf = calloc(1, sz + 3);565io_readn(io, buf, sz);566567switch (type) {568case RD_TEST:569if (dump_full)570printf("test: %s\n", (char *)buf);571break;572case RD_VERT_SHADER:573printf("vertex shader:\n%s\n", (char *)buf);574break;575case RD_FRAG_SHADER:576printf("fragment shader:\n%s\n", (char *)buf);577break;578case RD_PROGRAM: {579struct state state = {580.buf = buf,581.sz = sz,582};583printf(584"############################################################\n");585printf("program:\n");586dump_program(&state);587printf(588"############################################################\n");589break;590}591case RD_GPU_ID:592gpu_id = *((unsigned int *)buf);593printf("gpu_id: %d\n", gpu_id);594break;595default:596break;597}598}599600io_close(io);601602return 0;603}604605606