Commit f6117820 authored by Alyssa Rosenzweig's avatar Alyssa Rosenzweig

panfrost: Add pandecode (command stream debugger)

The `panwrap` utility can be LD_PRELOAD'd into a GLES app, intercepting
communication between the driver and the kernel. Modern panwrap versions
do no processing of their own; instead, they create a trace directory.
This directory contains the following files:

 - control.log: a line-by-line plain text file, denoting important
   syscalls (mmaps and job submits) along with their arguments

 - memory_*.bin, shader_*.bin: binary dumps of mapped memory

Together, these files contain enough information to reconstruct the
command stream and shaders of (at minimum) a single frame.

The `pandecode` utility takes this directory structure as input,
reconstructing the mapped memory and using the job submit command as an
entrypoint. It then walks the descriptors as the hardware would, parsing
and pretty-printing. Its final output is the pretty-printed command
stream interleaved with the disassembled shaders, suitable for driver
debugging. For instance, the behaviour of two driver versions (one
working, one broken) can be compared by diff'ing their decoded logs.

pandecode/decode.c was originally a part of `panwrap`; it is the oldest
living code in the project. Its history is generally not worth
preserving.

panwrap itself will continue to live downstream for the foreseeable
future, as it is specifically written for the vendor kernel. It is
possible, however, to produce equivalent traces directly from Panfrost,
bypassing the intermediate wrapping layer for well-behaved drivers.
Signed-off-by: Alyssa Rosenzweig's avatarAlyssa Rosenzweig <alyssa@rosenzweig.io>
parent fb3bbd0c
......@@ -119,3 +119,25 @@ midgard_compiler = executable(
],
build_by_default : true
)
files_pandecode = files(
'pandecode/cmdline.c',
'pandecode/decode.c',
'pan_pretty_print.c',
'midgard/disassemble.c'
)
pandecode = executable(
'pandecode',
files_pandecode,
include_directories : inc_panfrost,
dependencies : [
dep_thread,
],
link_with : [
libmesa_util
],
build_by_default : true
)
......@@ -27,11 +27,11 @@
#include <string.h>
#include <assert.h>
/* Some self-contained prettyprinting functions shared between panwrap and
/* Some self-contained prettyprinting functions shared between pandecode and
* the main driver */
#define DEFINE_CASE(name) case MALI_## name: return "MALI_" #name
char *panwrap_format_name(enum mali_format format)
char *pandecode_format_name(enum mali_format format)
{
static char unk_format_str[5];
......
......@@ -26,7 +26,7 @@
#include "panfrost-job.h"
char *panwrap_format_name(enum mali_format format);
char *pandecode_format_name(enum mali_format format);
void panfrost_print_blend_equation(struct mali_blend_equation eq);
#endif
/*
* Copyright (C) 2019 Alyssa Rosenzweig
* Copyright (C) 2017-2018 Lyude Paul
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "mmap.h"
int pandecode_replay_jc(mali_ptr jc_gpu_va, bool bifrost);
/* Memory handling */
static struct pandecode_mapped_memory mmaps;
struct pandecode_mapped_memory *
pandecode_find_mapped_gpu_mem_containing(mali_ptr addr)
{
list_for_each_entry(struct pandecode_mapped_memory, pos, &mmaps.node, node) {
if (addr >= pos->gpu_va && addr < pos->gpu_va + pos->length)
return pos;
}
return NULL;
}
char *
pointer_as_memory_reference(mali_ptr ptr)
{
struct pandecode_mapped_memory *mapped;
char *out = malloc(128);
/* Try to find the corresponding mapped zone */
mapped = pandecode_find_mapped_gpu_mem_containing(ptr);
if (mapped) {
snprintf(out, 128, "%s + %d", mapped->name, (int) (ptr - mapped->gpu_va));
return out;
}
/* Just use the raw address if other options are exhausted */
snprintf(out, 128, MALI_PTR_FMT, ptr);
return out;
}
/* Parsing */
static FILE *
pandecode_read_filename(const char *base, const char *name)
{
char *fn = NULL;
asprintf(&fn, "%s/%s", base, name);
FILE *fp = fopen(fn, "rb");
free(fn);
return fp;
}
static void
pandecode_read_memory(const char *base, const char *name, mali_ptr gpu_va)
{
FILE *fp = pandecode_read_filename(base, name);
if (!fp) {
fprintf(stderr, "Warning: missing %s\n", name);
return;
}
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *buf = malloc(sz);
assert(buf);
fread(buf, 1, sz, fp);
fclose(fp);
/* Now that we have the memory loaded in, create a mmap entry for it so
* we remember it later */
struct pandecode_mapped_memory *mapped_mem = NULL;
mapped_mem = malloc(sizeof(*mapped_mem));
list_inithead(&mapped_mem->node);
mapped_mem->gpu_va = gpu_va;
mapped_mem->length = sz;
mapped_mem->addr = buf;
memcpy(mapped_mem->name, name, strlen(name));
list_add(&mapped_mem->node, &mmaps.node);
}
static void
pandecode_read_mmap(const char *base, const char *line)
{
assert(strlen(line) < 500);
mali_ptr addr;
char name[512];
sscanf(line, "MMAP %lx %s", &addr, name);
pandecode_read_memory(base, name, addr);
}
static void
pandecode_read_job_submit(const char *base, const char *line)
{
mali_ptr addr;
unsigned core_req;
unsigned is_bifrost;
sscanf(line, "JS %lx %x %x", &addr, &core_req, &is_bifrost);
pandecode_replay_jc(addr, is_bifrost);
}
/* Reads the control file, processing as it goes. */
static void
pandecode_read_control(const char *base)
{
FILE *fp = pandecode_read_filename(base, "control.log");
if (!fp) {
fprintf(stderr, "Invalid directory path\n");
return;
}
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, fp) != -1) {
switch (line[0]) {
case 'M':
pandecode_read_mmap(base, line);
break;
case 'J':
pandecode_read_job_submit(base, line);
break;
default:
assert(0);
break;
}
}
}
int
main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "Usage: pandecode [directory]\n");
exit(1);
}
/* Initialize */
list_inithead(&mmaps.node);
/* Let's go! */
pandecode_read_control(argv[1]);
}
This diff is collapsed.
/*
* Copyright (C) 2017-2019 Lyude Paul
* Copyright (C) 2017-2019 Alyssa Rosenzweig
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#ifndef __MMAP_TRACE_H__
#define __MMAP_TRACE_H__
#include <stdlib.h>
#include <stddef.h>
#include <panfrost-job.h>
#include "util/list.h"
struct pandecode_mapped_memory {
struct list_head node;
size_t length;
void *addr;
mali_ptr gpu_va;
char name[32];
};
char *pointer_as_memory_reference(mali_ptr ptr);
struct pandecode_mapped_memory *pandecode_find_mapped_gpu_mem_containing(mali_ptr addr);
static inline void *
__pandecode_fetch_gpu_mem(const struct pandecode_mapped_memory *mem,
mali_ptr gpu_va, size_t size,
int line, const char *filename)
{
if (!mem)
mem = pandecode_find_mapped_gpu_mem_containing(gpu_va);
if (!mem ||
size + (gpu_va - mem->gpu_va) > mem->length)
assert(0);
return mem->addr + gpu_va - mem->gpu_va;
}
#define pandecode_fetch_gpu_mem(mem, gpu_va, size) \
__pandecode_fetch_gpu_mem(mem, gpu_va, size, __LINE__, __FILE__)
/* Returns a validated pointer to mapped GPU memory with the given pointer type,
* size automatically determined from the pointer type
*/
#define PANDECODE_PTR(mem, gpu_va, type) \
((type*)(__pandecode_fetch_gpu_mem(mem, gpu_va, sizeof(type), \
__LINE__, __FILE__)))
/* Usage: <variable type> PANDECODE_PTR_VAR(name, mem, gpu_va) */
#define PANDECODE_PTR_VAR(name, mem, gpu_va) \
name = __pandecode_fetch_gpu_mem(mem, gpu_va, sizeof(*name), \
__LINE__, __FILE__)
#endif /* __MMAP_TRACE_H__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment