freedreno/crashdec: Add option to export a snapshot

Add support to convert into the "snapshot" format used by internal
tooling.

Signed-off-by: Rob Clark <rob.clark@oss.qualcomm.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36426>
This commit is contained in:
Rob Clark 2025-07-24 08:28:37 -07:00 committed by Marge Bot
parent 08b9d771e3
commit a05b6e293c
2 changed files with 732 additions and 21 deletions

View file

@ -227,12 +227,16 @@ parseline_nowhitespace(const char *line, const char *fmt, ...)
static struct {
uint64_t iova;
uint32_t last_fence;
uint32_t retired_fence;
uint32_t rptr;
uint32_t wptr;
uint32_t size;
uint32_t *buf;
} ringbuffers[5];
#include "snapshot.h"
static void
decode_ringbuffer(void)
{
@ -244,6 +248,11 @@ decode_ringbuffer(void)
assert(id < ARRAY_SIZE(ringbuffers));
} else if (startswith(line, " iova:")) {
parseline(line, " iova: %" PRIx64, &ringbuffers[id].iova);
} else if (startswith(line, " last-fence:")) {
parseline(line, " last-fence: %u", &ringbuffers[id].last_fence);
} else if (startswith(line, " retired-fence:")) {
parseline(line, " retired-fence: %u", &ringbuffers[id].retired_fence);
} else if (startswith(line, " rptr:")) {
parseline(line, " rptr: %d", &ringbuffers[id].rptr);
} else if (startswith(line, " wptr:")) {
@ -254,6 +263,26 @@ decode_ringbuffer(void)
ringbuffers[id].buf = popline_ascii85(ringbuffers[id].size / 4);
add_buffer(ringbuffers[id].iova, ringbuffers[id].size,
ringbuffers[id].buf);
int n = snapshot_linux.ctxtcount;
if (n < ARRAY_SIZE(snapshot_contexts)) {
snapshot_contexts[n].id = id;
snapshot_contexts[n].timestamp_queued = ringbuffers[id].last_fence;
snapshot_contexts[n].timestamp_consumed = ringbuffers[id].retired_fence - 1;
snapshot_contexts[n].timestamp_retired = ringbuffers[id].retired_fence;
snapshot_rb[n].rbsize = ringbuffers[id].size / 4;
snapshot_rb[n].wptr = ringbuffers[id].wptr;
snapshot_rb[n].rptr = ringbuffers[id].rptr;
snapshot_rb[n].count = ringbuffers[id].size / 4;
snapshot_rb[n].timestamp_queued = ringbuffers[id].last_fence;
snapshot_rb[n].timestamp_retired = ringbuffers[id].retired_fence;
snapshot_rb[n].gpuaddr = ringbuffers[id].iova;
snapshot_rb[n].id = id;
snapshot_linux.ctxtcount++;
}
continue;
}
@ -268,8 +297,8 @@ decode_ringbuffer(void)
static void
decode_gmu_log(void)
{
uint64_t iova;
uint32_t size;
uint64_t iova = 0;
uint32_t size = 0;
foreach_line_in_section (line) {
if (startswith(line, " iova:")) {
@ -280,6 +309,7 @@ decode_gmu_log(void)
void *buf = popline_ascii85(size / 4);
dump_hex_ascii(buf, size, 1);
snapshot_gmu_mem(SNAPSHOT_GMU_MEM_LOG, iova, buf, size);
free(buf);
@ -325,6 +355,7 @@ decode_gmu_hfi(void)
dump_hex_ascii(hfi.buf, hfi.size, 1);
dump_gmu_hfi(&hfi);
snapshot_gmu_mem(SNAPSHOT_GMU_MEM_HFI, hfi.iova, hfi.buf, hfi.size);
free(hfi.buf);
@ -522,6 +553,7 @@ decode_bos(void)
dump_hex_ascii(buf, size, 1);
add_buffer(iova, size, buf);
snapshot_gpu_object(iova, size, buf);
continue;
}
@ -558,11 +590,19 @@ decode_gmu_registers(void)
uint32_t offset, value;
parseline(line, " - { offset: %x, value: %x }", &offset, &value);
assert(reg_buf.count < ARRAY_SIZE(reg_buf.regs));
reg_buf.regs[reg_buf.count].offset = offset / 4;
reg_buf.regs[reg_buf.count].value = value;
reg_buf.count++;
if (regacc_push(&r, offset / 4, value)) {
printf("\t%08"PRIx64"\t", r.value);
dump_register(&r);
}
}
snapshot_registers();
}
static void
@ -574,12 +614,20 @@ decode_registers(void)
uint32_t offset, value;
parseline(line, " - { offset: %x, value: %x }", &offset, &value);
assert(reg_buf.count < ARRAY_SIZE(reg_buf.regs));
reg_buf.regs[reg_buf.count].offset = offset / 4;
reg_buf.regs[reg_buf.count].value = value;
reg_buf.count++;
reg_set(offset / 4, value);
if (regacc_push(&r, offset / 4, value)) {
printf("\t%08"PRIx64, r.value);
dump_register_val(&r, 0);
}
}
snapshot_registers();
}
/* similar to registers section, but for banked context regs: */
@ -587,23 +635,49 @@ static void
decode_clusters(void)
{
struct regacc r = regacc(NULL);
char *cluster_name = NULL;
char *pipe_name = NULL;
uint32_t context = 0;
uint32_t location = ~0;
foreach_line_in_section (line) {
if (startswith_nowhitespace(line, "- cluster-name:") ||
startswith_nowhitespace(line, "- context:") ||
startswith_nowhitespace(line, "- pipe:")) {
printf("%s", line);
if (startswith_nowhitespace(line, "- cluster-name:")) {
free(cluster_name);
parseline_nowhitespace(line, "- cluster-name: %ms", &cluster_name);
location = ~0;
} else if (startswith_nowhitespace(line, "- context:")) {
parseline_nowhitespace(line, "- context: %u", &context);
} else if (startswith_nowhitespace(line, "- location:")) {
parseline_nowhitespace(line, "- location: %u", &location);
} else if (startswith_nowhitespace(line, "- pipe:")) {
snapshot_cluster_regs(pipe_name, cluster_name, context, location);
free(pipe_name);
parseline_nowhitespace(line, "- pipe: %ms", &pipe_name);
} else {
uint32_t offset, value;
parseline_nowhitespace(line, "- { offset: %x, value: %x }", &offset, &value);
assert(reg_buf.count < ARRAY_SIZE(reg_buf.regs));
reg_buf.regs[reg_buf.count].offset = offset / 4;
reg_buf.regs[reg_buf.count].value = value;
reg_buf.count++;
if (regacc_push(&r, offset / 4, value)) {
printf("\t%08"PRIx64, r.value);
dump_register_val(&r, 0);
}
continue;
}
uint32_t offset, value;
parseline_nowhitespace(line, "- { offset: %x, value: %x }", &offset, &value);
if (regacc_push(&r, offset / 4, value)) {
printf("\t%08"PRIx64, r.value);
dump_register_val(&r, 0);
}
printf("%s", line);
}
snapshot_cluster_regs(pipe_name, cluster_name, context, location);
free(cluster_name);
free(pipe_name);
}
/*
@ -729,6 +803,7 @@ const static struct {
} index_reg_renames[] = {
{"CP_ROQ", "CP_ROQ_DBG"},
{"CP_UCODE_DBG_DATA", "CP_SQE_UCODE_DBG"},
{"CP_UCODE_DBG", "CP_SQE_UCODE_DBG"},
{"CP_RESOURCE_TBL", "CP_RESOURCE_TABLE_DBG"},
{"CP_LPAC_ROQ", "CP_LPAC_ROQ_DBG"},
{"CP_BV_DRAW_STATE_ADDR", "CP_BV_DRAW_STATE"},
@ -778,16 +853,18 @@ decode_indexed_registers(void)
if (!strcmp(name, "CP_SQE_STAT") || !strcmp(name, "CP_BV_SQE_STAT"))
dump_cp_sqe_stat(buf);
if (!strcmp(name, "CP_UCODE_DBG") ||
if (!strcmp(name, "CP_SQE_UCODE_DBG") ||
!strcmp(name, "CP_BV_SQE_UCODE_DBG"))
dump_cp_ucode_dbg(buf);
if (!strcmp(name, "CP_MEMPOOL"))
if (!strcmp(name, "CP_MEM_POOL_DBG"))
dump_cp_mem_pool(buf);
if (dump)
dump_hex_ascii(buf, 4 * sizedwords, 1);
snapshot_indexed_regs(name, buf, sizedwords);
free(buf);
continue;
@ -805,12 +882,32 @@ static void
decode_shader_blocks(void)
{
char *type = NULL;
char *pipe = NULL;
int sp = 0;
int usptp = 0;
/* NOTE: earlier kernels do not report the location. But conveniently
* all entries before A7XX_HLSQ_DATAPATH_DSTR_META are USPTP (3) and
* the other entries are HLSQ_STATE (0), so we can implement a work-
* around.
*/
int location = 3; /* A7XX_USPTP */
uint32_t sizedwords = 0;
foreach_line_in_section (line) {
if (startswith(line, " - type:")) {
free(type);
parseline(line, " - type: %ms", &type);
if (!strcmp(type, "A7XX_HLSQ_DATAPATH_DSTR_META"))
location = 0; /* A7XX_HLSQ_STATE */
} else if (startswith_nowhitespace(line, "- pipe:")) {
free(pipe);
parseline_nowhitespace(line, "- pipe: %ms", &pipe);
} else if (startswith_nowhitespace(line, "- location:")) {
parseline_nowhitespace(line, "- location: %d", &location);
} else if (startswith_nowhitespace(line, "- sp:")) {
parseline_nowhitespace(line, "- sp: %d", &sp);
} else if (startswith_nowhitespace(line, "- usptp:")) {
parseline_nowhitespace(line, "- usptp: %d", &usptp);
} else if (startswith_nowhitespace(line, "size:")) {
parseline_nowhitespace(line, "size: %u", &sizedwords);
} else if (startswith_nowhitespace(line, "data: !!ascii85 |")) {
@ -838,6 +935,8 @@ decode_shader_blocks(void)
if (dump)
dump_hex_ascii(buf, 4 * sizedwords, 1);
snapshot_shader_block(type, pipe, sp, usptp, location, buf, sizedwords);
free(buf);
continue;
@ -847,6 +946,7 @@ decode_shader_blocks(void)
}
free(type);
free(pipe);
}
/*
@ -875,6 +975,7 @@ decode_debugbus(void)
if (dump)
dump_hex_ascii(buf, 4 * sizedwords, 1);
snapshot_debugbus(block, buf, sizedwords);
free(buf);
@ -896,7 +997,18 @@ decode(void)
while ((line = popline())) {
printf("%s", line);
if (startswith(line, "revision:")) {
if (startswith(line, "kernel:")) {
char *release = NULL;
parseline(line, "kernel: %ms", &release);
strncpy((char *)snapshot_linux.release, release, sizeof(snapshot_linux.release) - 1);
free(release);
} else if (startswith(line, "time:")) {
double time;
parseline(line, "time: %d", &time);
snapshot_linux.seconds = (uint32_t)time;
} else if (startswith(line, "revision:")) {
unsigned core, major, minor, patchid;
parseline(line, "revision: %u (%u.%u.%u.%u)", &options.dev_id.gpu_id,
@ -943,6 +1055,8 @@ decode(void)
} else {
rnn_control = NULL;
}
snapshot_write_header(options.dev_id.chip_id);
} else if (startswith(line, "fault-info:")) {
decode_fault_info();
} else if (startswith(line, "bos:")) {
@ -959,7 +1073,8 @@ decode(void)
/* after we've recorded buffer contents, and CP register values,
* we can take a stab at decoding the cmdstream:
*/
dump_cmdstream();
if (!snapshot)
dump_cmdstream();
} else if (startswith(line, "registers-gmu:")) {
decode_gmu_registers();
} else if (startswith(line, "indexed-registers:")) {
@ -970,6 +1085,7 @@ decode(void)
decode_clusters();
} else if (startswith(line, "debugbus:")) {
decode_debugbus();
do_snapshot();
}
}
}
@ -983,7 +1099,7 @@ usage(void)
{
/* clang-format off */
fprintf(stderr, "Usage:\n\n"
"\tcrashdec [-achmsv] [-f FILE]\n\n"
"\tcrashdec [-achmsv] [-f FILE] [-S FILE]\n\n"
"Options:\n"
"\t-a, --allregs - show all registers (including ones not written since\n"
"\t previous draw) at each draw\n"
@ -991,6 +1107,7 @@ usage(void)
"\t-f, --file=FILE - read input from specified file (rather than stdin)\n"
"\t-h, --help - this usage message\n"
"\t-m, --markers - try to decode CP_NOP string markers\n"
"\t-S, --snapshot - export crashdump to snapshot format\n"
"\t-s, --summary - don't show individual register writes, but just show\n"
"\t register values on draws\n"
"\t-v, --verbose - dump more verbose output, including contents of\n"
@ -1008,6 +1125,7 @@ static const struct option opts[] = {
{ .name = "file", .has_arg = 1, NULL, 'f' },
{ .name = "help", .has_arg = 0, NULL, 'h' },
{ .name = "markers", .has_arg = 0, NULL, 'm' },
{ .name = "snapshot",.has_arg = 1, NULL, 'S' },
{ .name = "summary", .has_arg = 0, NULL, 's' },
{ .name = "verbose", .has_arg = 0, NULL, 'v' },
{}
@ -1037,7 +1155,7 @@ main(int argc, char **argv)
/* default to read from stdin: */
in = stdin;
while ((c = getopt_long(argc, argv, "acf:l:hmsv", opts, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "acf:l:hmS:sv", opts, NULL)) != -1) {
switch (c) {
case 'a':
options.allregs = true;
@ -1054,6 +1172,9 @@ main(int argc, char **argv)
case 'm':
options.decode_markers = true;
break;
case 'S':
snapshot = fopen(optarg, "w");
break;
case 's':
options.summary = true;
break;
@ -1068,7 +1189,9 @@ main(int argc, char **argv)
disasm_a3xx_set_debug(PRINT_RAW);
if (interactive) {
if (snapshot) {
freopen("/dev/null", "w", stdout);
} else if (interactive) {
pager_open();
}

View file

@ -0,0 +1,588 @@
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its susidiaries.
* SPDX-License-Identifier: MIT
*/
#ifndef __SNAPSHOT_H__
#define __SNAPSHOT_H__
/*
* Based on kgsl_snapshot.h
*/
#include <stdint.h>
#include <string.h>
#include "util/macros.h"
/* High word is static, low word is snapshot version ID */
#define SNAPSHOT_MAGIC 0x504D0002
#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+ sizeof(struct snapshot_debug))
/* GPU ID scheme:
* [16:31] - core identifer (0x0002 for 2D or 0x0003 for 3D)
* [00:16] - GPU specific identifier
*/
struct snapshot_header {
uint32_t magic; /* Magic identifier */
uint32_t gpuid; /* GPU ID - see above */
/* Added in snapshot version 2 */
uint32_t chipid; /* Chip ID from the GPU */
} PACKED;
/* Section header */
#define SNAPSHOT_SECTION_MAGIC 0xABCD
struct snapshot_section_header {
uint16_t magic; /* Magic identifier */
uint16_t id; /* Type of section */
uint32_t size; /* Size of the section including this header */
} PACKED;
/* Section identifiers */
#define SNAPSHOT_SECTION_OS 0x0101
#define SNAPSHOT_SECTION_REGS 0x0201
#define SNAPSHOT_SECTION_REGS_V2 0x0202
#define SNAPSHOT_SECTION_RB_V2 0x0302
#define SNAPSHOT_SECTION_IB_V2 0x0402
#define SNAPSHOT_SECTION_INDEXED_REGS 0x0501
#define SNAPSHOT_SECTION_INDEXED_REGS_V2 0x0502
#define SNAPSHOT_SECTION_DEBUG 0x0901
#define SNAPSHOT_SECTION_DEBUGBUS 0x0A01
#define SNAPSHOT_SECTION_GPU_OBJECT_V2 0x0B02
#define SNAPSHOT_SECTION_MEMLIST_V2 0x0E02
#define SNAPSHOT_SECTION_SHADER 0x1201
#define SNAPSHOT_SECTION_SHADER_V2 0x1202
#define SNAPSHOT_SECTION_SHADER_V3 0x1203
#define SNAPSHOT_SECTION_MVC 0x1501
#define SNAPSHOT_SECTION_MVC_V2 0x1502
#define SNAPSHOT_SECTION_MVC_V3 0x1503
#define SNAPSHOT_SECTION_GMU_MEMORY 0x1701
#define SNAPSHOT_SECTION_SIDE_DEBUGBUS 0x1801
#define SNAPSHOT_SECTION_TRACE_BUFFER 0x1901
#define SNAPSHOT_SECTION_EVENTLOG 0x1A01
#define SNAPSHOT_SECTION_END 0xFFFF
/* OS sub-section header */
#define SNAPSHOT_OS_LINUX_V4 0x00000203
/* Linux OS specific information */
struct snapshot_linux_v4 {
int osid; /* subsection OS identifier */
uint32_t seconds; /* Unix timestamp for the snapshot */
uint32_t power_flags; /* Current power flags */
uint32_t power_level; /* Current power level */
uint32_t power_interval_timeout; /* Power interval timeout */
uint32_t grpclk; /* Current GP clock value */
uint32_t busclk; /* Current busclk value */
uint64_t ptbase; /* Current ptbase */
uint64_t ptbase_lpac; /* Current LPAC ptbase */
uint32_t pid; /* PID of the process that owns the PT */
uint32_t pid_lpac; /* PID of the LPAC process that owns the PT */
uint32_t current_context; /* ID of the current context */
uint32_t current_context_lpac; /* ID of the current LPAC context */
uint32_t ctxtcount; /* Number of contexts appended to section */
unsigned char release[32]; /* kernel release */
unsigned char version[32]; /* kernel version */
unsigned char comm[16]; /* Name of the process that owns the PT */
unsigned char comm_lpac[16]; /* Name of the LPAC process that owns the PT */
} PACKED;
/*
* This structure contains a record of an active context.
* These are appended one after another in the OS section below
* the header above
*/
struct snapshot_linux_context_v2 {
uint32_t id; /* The context ID */
uint32_t timestamp_queued; /* The last queued timestamp */
uint32_t timestamp_consumed; /* The last timestamp consumed by HW */
uint32_t timestamp_retired; /* The last timestamp retired by HW */
};
/* Ringbuffer sub-section header */
struct snapshot_rb_v2 {
int start; /* dword at the start of the dump */
int end; /* dword at the end of the dump */
int rbsize; /* Size (in dwords) of the ringbuffer */
int wptr; /* Current index of the CPU write pointer */
int rptr; /* Current index of the GPU read pointer */
int count; /* Number of dwords in the dump */
uint32_t timestamp_queued; /* The last queued timestamp */
uint32_t timestamp_retired; /* The last timestamp retired by HW */
uint64_t gpuaddr; /* The GPU address of the ringbuffer */
uint32_t id; /* Ringbuffer identifier */
} PACKED;
/* Replay or Memory list section, both sections have same header */
struct snapshot_mem_list_v2 {
/*
* Number of IBs to replay for replay section or
* number of memory list entries for mem list section
*/
int num_entries;
/* Pagetable base to which the replay IBs or memory entries belong */
uint64_t ptbase;
} PACKED;
/* Indirect buffer sub-section header (v2) */
struct snapshot_ib_v2 {
uint64_t gpuaddr; /* GPU address of the IB */
uint64_t ptbase; /* Base for the pagetable the GPU address is valid in */
uint64_t size; /* Size of the IB */
} PACKED;
/* GMU memory ID's */
#define SNAPSHOT_GMU_MEM_UNKNOWN 0x00
#define SNAPSHOT_GMU_MEM_HFI 0x01
#define SNAPSHOT_GMU_MEM_LOG 0x02
#define SNAPSHOT_GMU_MEM_BWTABLE 0x03
#define SNAPSHOT_GMU_MEM_DEBUG 0x04
#define SNAPSHOT_GMU_MEM_BIN_BLOCK 0x05
#define SNAPSHOT_GMU_MEM_CONTEXT_QUEUE 0x06
#define SNAPSHOT_GMU_MEM_HW_FENCE 0x07
#define SNAPSHOT_GMU_MEM_WARMBOOT 0x08
#define SNAPSHOT_GMU_MEM_VRB 0x09
#define SNAPSHOT_GMU_MEM_TRACE 0x0a
/* GMU memory section data */
struct snapshot_gmu_mem {
int type;
uint64_t hostaddr;
uint64_t gmuaddr;
uint64_t gpuaddr;
} PACKED;
/* Register sub-section header */
struct snapshot_regs {
uint32_t count; /* Number of register pairs in the section */
} PACKED;
/* Indexed register sub-section header */
struct snapshot_indexed_regs {
uint32_t index_reg; /* Offset of the index register for this section */
uint32_t data_reg; /* Offset of the data register for this section */
int start; /* Starting index */
int count; /* Number of dwords in the data */
} PACKED;
struct snapshot_indexed_regs_v2 {
uint32_t index_reg; /* Offset of the index register for this section */
uint32_t data_reg; /* Offset of the data register for this section */
uint32_t start; /* Starting index */
uint32_t count; /* Number of dwords in the data */
uint32_t pipe_id; /* Id of pipe, BV, Br etc */
uint32_t slice_id; /* Slice ID to be dumped */
} PACKED;
/* MVC register sub-section header */
struct snapshot_mvc_regs {
int ctxt_id;
int cluster_id;
} PACKED;
struct snapshot_mvc_regs_v2 {
int ctxt_id;
int cluster_id;
int pipe_id;
int location_id;
} PACKED;
struct snapshot_mvc_regs_v3 {
uint32_t ctxt_id;
uint32_t cluster_id;
uint32_t pipe_id;
uint32_t location_id;
uint32_t slice_id;
uint32_t sp_id;
uint32_t usptp_id;
} PACKED;
/* Debug data sub-section header */
/* A5XX debug sections */
#define SNAPSHOT_DEBUG_CP_MEQ 7
#define SNAPSHOT_DEBUG_CP_PM4_RAM 8
#define SNAPSHOT_DEBUG_CP_PFP_RAM 9
#define SNAPSHOT_DEBUG_CP_ROQ 10
#define SNAPSHOT_DEBUG_SHADER_MEMORY 11
#define SNAPSHOT_DEBUG_CP_MERCIU 12
#define SNAPSHOT_DEBUG_SQE_VERSION 14
/* GMU Version information */
#define SNAPSHOT_DEBUG_GMU_CORE_VERSION 15
#define SNAPSHOT_DEBUG_GMU_CORE_DEV_VERSION 16
#define SNAPSHOT_DEBUG_GMU_PWR_VERSION 17
#define SNAPSHOT_DEBUG_GMU_PWR_DEV_VERSION 18
#define SNAPSHOT_DEBUG_GMU_HFI_VERSION 19
#define SNAPSHOT_DEBUG_AQE_VERSION 20
struct snapshot_debug {
int type; /* Type identifier for the attached tata */
int size; /* Size of the section in dwords */
} PACKED;
struct snapshot_debugbus {
int id; /* Debug bus ID */
int count; /* Number of dwords in the dump */
} PACKED;
struct snapshot_side_debugbus {
int id; /* Debug bus ID */
int size; /* Number of dwords in the dump */
int valid_data; /* Mask of valid bits of the side debugbus */
} PACKED;
struct snapshot_shader {
int type; /* SP/TP statetype */
int index; /* SP/TP index */
int size; /* Number of dwords in the dump */
} PACKED;
struct snapshot_shader_v2 {
int type; /* SP/TP statetype */
int index; /* SP/TP index */
int usptp; /* USPTP index */
int pipe_id; /* Pipe id */
int location; /* Location value */
uint32_t size; /* Number of dwords in the dump */
} PACKED;
struct snapshot_shader_v3 {
uint32_t type; /* SP/TP statetype */
uint32_t slice_id; /* Slice ID */
uint32_t sp_index; /* SP/TP index */
uint32_t usptp; /* USPTP index */
uint32_t pipe_id; /* Pipe id */
uint32_t location; /* Location value */
uint32_t ctxt_id; /* Context ID */
uint32_t size; /* Number of dwords in the dump */
} PACKED;
#define TRACE_BUF_NUM_SIG 4
/**
* enum trace_buffer_source - Bits to identify the source block of trace buffer information
* GX_DBGC : Signals captured from GX block
* CX_DBGC : Signals captured from CX block
*/
enum trace_buffer_source {
GX_DBGC = 1,
CX_DBGC = 2,
};
/**
* snapshot_trace_buffer: Header Information for the tracebuffer in snapshot.
*/
struct snapshot_trace_buffer {
/** @dbgc_ctrl: Identify source for trace */
uint16_t dbgc_ctrl;
/** @dbgc_ctrl: Identify source for trace */
uint16_t segment;
/** @granularity: The total number of segments in each packet */
uint16_t granularity;
/** @ping_blk: Signal block */
uint16_t ping_blk[TRACE_BUF_NUM_SIG];
/** @ping_idx: Signal Index */
uint16_t ping_idx[TRACE_BUF_NUM_SIG];
/** @size: Number of bytes in the dump */
uint32_t size;
} PACKED;
#define SNAPSHOT_GPU_OBJECT_SHADER 1
#define SNAPSHOT_GPU_OBJECT_IB 2
#define SNAPSHOT_GPU_OBJECT_GENERIC 3
#define SNAPSHOT_GPU_OBJECT_DRAW 4
#define SNAPSHOT_GPU_OBJECT_GLOBAL 5
struct snapshot_gpu_object_v2 {
int type; /* Type of GPU object */
uint64_t gpuaddr; /* GPU address of the object */
uint64_t ptbase; /* Base for the pagetable the GPU address is valid in */
uint64_t size; /* Size of the object (in dwords) */
} PACKED;
struct snapshot_eventlog {
/** @type: Type of the event log buffer */
uint16_t type;
/** @version: Version of the event log buffer */
uint16_t version;
/** @size: Size of the eventlog buffer in bytes */
uint32_t size;
} PACKED;
struct snapshot_gmu_version {
/** @type: Type of the GMU version buffer */
uint32_t type;
/** @value: GMU FW version value */
uint32_t value;
} PACKED;
/*
* Helpers to write snapshots below here:
*/
static FILE *snapshot;
static inline void
snapshot_write(void *data, size_t sz)
{
fwrite(data, sz, 1, snapshot);
}
static inline void
snapshot_write_sect_header(uint16_t sect_id, size_t sz)
{
struct snapshot_section_header sect_hdr = {
.magic = SNAPSHOT_SECTION_MAGIC,
.id = sect_id,
.size = sizeof(sect_hdr) + sz,
};
snapshot_write(&sect_hdr, sizeof(sect_hdr));
}
static inline void
snapshot_write_header(uint32_t chip_id)
{
if (!snapshot)
return;
struct snapshot_header hdr = {
.magic = SNAPSHOT_MAGIC,
.gpuid = ~0,
.chipid = chip_id,
};
snapshot_write(&hdr, sizeof(hdr));
}
static struct snapshot_linux_v4 snapshot_linux = {
.osid = SNAPSHOT_OS_LINUX_V4,
};
static struct snapshot_linux_context_v2 snapshot_contexts[16];
static struct snapshot_rb_v2 snapshot_rb[16];
static inline void
snapshot_gmu_mem(int type, uint64_t iova, uint32_t *buf, uint32_t size)
{
if (!snapshot)
return;
struct snapshot_gmu_mem gmu_mem = {
.type = type,
.gmuaddr = iova,
.gpuaddr = 0,
.hostaddr = 0,
};
snapshot_write_sect_header(
SNAPSHOT_SECTION_GMU_MEMORY,
sizeof(gmu_mem) + size
);
snapshot_write(&gmu_mem, sizeof(gmu_mem));
snapshot_write(buf, size);
}
/* Not directly part of the snapshot, but used to generate the register
* snapshot sections:
*/
#define MAX_REGS 2500
struct reg_buf {
uint32_t count;
struct {
uint32_t offset;
uint32_t value;
} regs[10000];
};
static struct reg_buf reg_buf;
static inline void
snapshot_registers(void)
{
uint32_t count = reg_buf.count;
if (!count)
return;
reg_buf.count = 0;
if (!snapshot)
return;
struct snapshot_regs regs = {
.count = count,
};
snapshot_write_sect_header(
SNAPSHOT_SECTION_REGS,
sizeof(regs) + (8 * count)
);
snapshot_write(&regs, sizeof(regs));
snapshot_write(reg_buf.regs, 8 * count);
}
static inline void
snapshot_indexed_regs(const char *name, uint32_t *regs, uint32_t sizedwords)
{
if (!snapshot)
return;
char addr_reg[64];
char data_reg[64];
strcpy(addr_reg, name);
strcpy(&addr_reg[strlen(name)], "_ADDR");
strcpy(data_reg, name);
strcpy(&data_reg[strlen(name)], "_DATA");
/* Todo, 8xx should use snapshot_indexed_regs_v2.. which needs more
* info from kernel:
*/
struct snapshot_indexed_regs index_regs = {
.index_reg = regbase(addr_reg),
.data_reg = regbase(data_reg),
.count = sizedwords,
};
snapshot_write_sect_header(
SNAPSHOT_SECTION_INDEXED_REGS,
sizeof(index_regs) + (4 * sizedwords)
);
snapshot_write(&index_regs, sizeof(index_regs));
snapshot_write(regs, 4 * sizedwords);
}
static inline void
snapshot_cluster_regs(const char *pipe_name, const char *cluster_name, int context,
uint32_t location)
{
uint32_t count = reg_buf.count;
if (!count)
return;
reg_buf.count = 0;
if (!snapshot)
return;
/* TODO, 8xx should use snapshot_mvc_regs_v3: */
struct snapshot_mvc_regs_v2 cluster_regs = {
.ctxt_id = context,
.cluster_id = enumval("a7xx_cluster", cluster_name),
.pipe_id = enumval("a7xx_pipe", pipe_name),
.location_id = location,
};
snapshot_write_sect_header(
SNAPSHOT_SECTION_MVC_V2,
sizeof(cluster_regs) + (8 * count)
);
snapshot_write(&cluster_regs, sizeof(cluster_regs));
snapshot_write(reg_buf.regs, 8 * count);
}
static inline void
snapshot_debugbus(const char *block, uint32_t *buf, uint32_t sizedwords)
{
if (!snapshot)
return;
struct snapshot_debugbus debugbus = {
.id = enumval("a7xx_debugbus_id", block),
.count = sizedwords,
};
snapshot_write_sect_header(
SNAPSHOT_SECTION_DEBUGBUS,
sizeof(debugbus) + (4 * sizedwords)
);
snapshot_write(&debugbus, sizeof(debugbus));
snapshot_write(buf, 4 * sizedwords);
}
static inline void
snapshot_shader_block(const char *type, const char *pipe, int sp, int usptp,
int location, uint32_t *buf, uint32_t sizedwords)
{
if (!snapshot)
return;
/* TODO, 8xx should use snapshot_shader_v3: */
struct snapshot_shader_v2 shader_block = {
.type = enumval("a7xx_statetype_id", type),
.index = sp,
.usptp = usptp,
.pipe_id = enumval("a7xx_pipe", pipe),
.location = location,
.size = sizedwords,
};
snapshot_write_sect_header(
SNAPSHOT_SECTION_SHADER_V2,
sizeof(shader_block) + (4 * sizedwords)
);
snapshot_write(&shader_block, sizeof(shader_block));
snapshot_write(buf, 4 * sizedwords);
}
static inline void
snapshot_gpu_object(uint64_t gpuaddr, uint32_t size, uint32_t *buf)
{
if (!snapshot)
return;
struct snapshot_gpu_object_v2 gpu_object = {
.type = SNAPSHOT_GPU_OBJECT_GENERIC,
.gpuaddr = gpuaddr,
.ptbase = 0, /* We don't have this.. use magic value? */
.size = size / 4, /* dwords */
};
snapshot_write_sect_header(
SNAPSHOT_SECTION_GPU_OBJECT_V2,
sizeof(gpu_object) + size
);
snapshot_write(&gpu_object, sizeof(gpu_object));
snapshot_write(buf, size);
}
static inline void
do_snapshot(void)
{
if (!snapshot)
return;
snapshot_write_sect_header(
SNAPSHOT_SECTION_OS,
sizeof(snapshot_linux) + (snapshot_linux.ctxtcount * sizeof(snapshot_contexts[0]))
);
snapshot_write(&snapshot_linux, sizeof(snapshot_linux));
snapshot_write(snapshot_contexts, snapshot_linux.ctxtcount * sizeof(snapshot_contexts[0]));
for (unsigned i = 0; i < snapshot_linux.ctxtcount; i++) {
snapshot_write_sect_header(
SNAPSHOT_SECTION_RB_V2,
sizeof(snapshot_rb[i]) + (4 * snapshot_rb[i].rbsize)
);
snapshot_write(&snapshot_rb[i], sizeof(snapshot_rb[i]));
int id = snapshot_rb[i].id;
void *buf = ringbuffers[id].buf;
snapshot_write(buf, snapshot_rb[i].rbsize * 4);
}
snapshot_write_sect_header(SNAPSHOT_SECTION_END, 0);
}
#endif /* __SNAPSHOT_H__ */