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:
parent
08b9d771e3
commit
a05b6e293c
2 changed files with 732 additions and 21 deletions
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
588
src/freedreno/decode/snapshot.h
Normal file
588
src/freedreno/decode/snapshot.h
Normal 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(§_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(®s, 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__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue