ir3: add shader bisect debug tool

When debugging a problem in a trace, CTS test,... that is caused by a
known compiler feature, the first step is usually to find which shader
causes the problem. This is often non-trivial as the amount of shaders
in a trace can be huge. This commit adds a debugging tool to help with
this.

The idea behind this tool is to assign every shader a deterministic
(pre-compilation) ID that can be used to order shaders. Once we have
this, we can use it to bisect which shader causes the problem. This
obviously only works if the problem can be traced back to a single
shader. In my experience, this is often the case.

This tool reuses the shader cache key as deterministic ID. It is
concatenated with the variant ID to distinguish the different variants
of a shader.

In practice, bisecting the shaders in a test run works like this:
- Gate the problematic compiler feature using ir3_shader_bisect_select;
  E.g., if (ir3_shader_bisect_select(v)) IR3_PASS(...);
- Run test with IR3_SHADER_BISECT_DUMP_IDS_PATH=ids.txt
- Sort ids.txt
- Bisect the shader IDs using IR3_SHADER_BISECT_LO/IR3_SHADER_BISECT_HI.
- Dump the problematic shader using IR3_SHADER_BISECT_DISASM.

A Python script is provided to make all this easier:
- ir3_shader_bisect.py dump-ids -o ids.txt 'test args'
- ir3_shader_bisect.py bisect -i ids.txt 'test args'

Signed-off-by: Job Noorman <jnoorman@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33602>
This commit is contained in:
Job Noorman
2025-02-18 13:58:05 +01:00
committed by Marge Bot
parent 0a123ce68b
commit b101aecb03
8 changed files with 179 additions and 2 deletions

View File

@@ -144,6 +144,8 @@ ir3_compiler_create(struct fd_device *dev, const struct fd_dev_id *dev_id,
ir3_shader_debug |= IR3_DBG_NOCACHE;
}
ir3_shader_bisect_init();
compiler->dev = dev;
compiler->dev_id = dev_id;
compiler->gen = fd_dev_gen(dev_id);

View File

@@ -449,6 +449,12 @@ ir3_shader_debug_hash_key()
const char *
ir3_shader_debug_as_string(void);
void ir3_shader_bisect_init(void);
bool ir3_shader_bisect_need_shader_key(void);
void ir3_shader_bisect_dump_id(struct ir3_shader_variant *v);
bool ir3_shader_bisect_select(struct ir3_shader_variant *v);
bool ir3_shader_bisect_disasm_select(struct ir3_shader_variant *v);
ENDC;
#endif /* IR3_COMPILER_H_ */

View File

@@ -5574,6 +5574,8 @@ ir3_compile_shader_nir(struct ir3_compiler *compiler,
int ret = 0, max_bary;
bool progress;
ir3_shader_bisect_dump_id(so);
MESA_TRACE_FUNC();
assert(!so->ir);

View File

@@ -61,7 +61,7 @@ void
ir3_disk_cache_init_shader_key(struct ir3_compiler *compiler,
struct ir3_shader *shader)
{
if (!compiler->disk_cache)
if (!compiler->disk_cache && !ir3_shader_bisect_need_shader_key())
return;
struct mesa_sha1 ctx;

View File

@@ -406,7 +406,8 @@ assemble_variant(struct ir3_shader_variant *v, bool internal)
_mesa_sha1_final(&ctx, sha1);
_mesa_sha1_format(v->sha1_str, sha1);
bool dbg_enabled = shader_debug_enabled(v->type, internal);
bool dbg_enabled = shader_debug_enabled(v->type, internal) ||
ir3_shader_bisect_disasm_select(v);
if (dbg_enabled || ir3_shader_override_path || v->disasm_info.write_disasm) {
bool shader_overridden =
ir3_shader_override_path && try_override_shader_variant(v, v->sha1_str);

View File

@@ -0,0 +1,98 @@
#include "ir3_shader.h"
#include "util/hex.h"
static const char *ir3_shader_bisect_dump_ids_path = NULL;
static const char *ir3_shader_bisect_lo = NULL;
static const char *ir3_shader_bisect_hi = NULL;
static const char *ir3_shader_bisect_disasm = NULL;
DEBUG_GET_ONCE_OPTION(ir3_shader_bisect_dump_ids_path,
"IR3_SHADER_BISECT_DUMP_IDS_PATH", NULL);
DEBUG_GET_ONCE_OPTION(ir3_shader_bisect_lo, "IR3_SHADER_BISECT_LO", NULL);
DEBUG_GET_ONCE_OPTION(ir3_shader_bisect_hi, "IR3_SHADER_BISECT_HI", NULL);
DEBUG_GET_ONCE_OPTION(ir3_shader_bisect_disasm, "IR3_SHADER_BISECT_DISASM",
NULL);
#define BISECT_ID_SIZE (2 * (CACHE_KEY_SIZE + 1) + 1)
void
ir3_shader_bisect_init()
{
ir3_shader_bisect_dump_ids_path =
__normal_user() ? debug_get_option_ir3_shader_bisect_dump_ids_path()
: NULL;
ir3_shader_bisect_lo = debug_get_option_ir3_shader_bisect_lo();
ir3_shader_bisect_hi = debug_get_option_ir3_shader_bisect_hi();
ir3_shader_bisect_disasm = debug_get_option_ir3_shader_bisect_disasm();
if (ir3_shader_bisect_dump_ids_path) {
FILE *f = fopen(ir3_shader_bisect_dump_ids_path, "w");
assert(f && "Failed to open ir3_shader_bisect_dump_ids_path");
fclose(f);
}
}
bool
ir3_shader_bisect_need_shader_key()
{
return ir3_shader_bisect_dump_ids_path || ir3_shader_bisect_lo ||
ir3_shader_bisect_hi || ir3_shader_bisect_disasm;
}
static void
get_shader_bisect_id(struct ir3_shader_variant *v, char id[BISECT_ID_SIZE])
{
uint8_t id_bin[sizeof(v->shader->cache_key) + 1];
memcpy(id_bin, v->shader->cache_key, sizeof(v->shader->cache_key));
id_bin[sizeof(v->shader->cache_key)] = v->id;
mesa_bytes_to_hex(id, id_bin, sizeof(id_bin));
}
void
ir3_shader_bisect_dump_id(struct ir3_shader_variant *v)
{
if (!ir3_shader_bisect_dump_ids_path) {
return;
}
FILE *f = fopen(ir3_shader_bisect_dump_ids_path, "a");
assert(f);
char id[BISECT_ID_SIZE];
get_shader_bisect_id(v, id);
fprintf(f, "%s\n", id);
}
bool
ir3_shader_bisect_select(struct ir3_shader_variant *v)
{
if (!ir3_shader_bisect_lo && !ir3_shader_bisect_hi) {
return false;
}
char id[BISECT_ID_SIZE];
get_shader_bisect_id(v, id);
if (ir3_shader_bisect_lo && strcmp(id, ir3_shader_bisect_lo) < 0) {
return false;
}
if (ir3_shader_bisect_hi && strcmp(id, ir3_shader_bisect_hi) > 0) {
return false;
}
return true;
}
bool
ir3_shader_bisect_disasm_select(struct ir3_shader_variant *v)
{
if (!ir3_shader_bisect_disasm) {
return false;
}
char id[BISECT_ID_SIZE];
get_shader_bisect_id(v, id);
return strcmp(ir3_shader_bisect_disasm, id) == 0;
}

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python3
import argparse
import subprocess
import os
def run(cmd, extra_env):
env = os.environ | extra_env
env['MESA_SHADER_CACHE_DISABLE'] = '1'
subprocess.run(cmd, env=env, shell=True)
def dump(args):
extra_env = {'IR3_SHADER_BISECT_DUMP_IDS_PATH': args.output}
run(args.cmd, extra_env)
def was_good():
while True:
response = input('Was the previous run [g]ood or [b]ad? ')
if response in ('g', 'b'):
return response == 'g'
def bisect(args):
with open(args.input, 'r') as f:
ids = [l.strip() for l in f.readlines()]
ids.sort()
while len(ids) > 1:
lo_id = 0
hi_id = len(ids) // 2
lo = ids[lo_id]
hi = ids[hi_id]
extra_env = {
'IR3_SHADER_BISECT_LO': str(lo),
'IR3_SHADER_BISECT_HI': str(hi)
}
run(args.cmd, extra_env)
if was_good():
del ids[lo_id:hi_id + 1]
else:
del ids[hi_id:]
print(ids)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(required=True)
gather_parser = subparsers.add_parser('dump-ids')
gather_parser.add_argument('-o', '--output', required=True)
gather_parser.add_argument('cmd')
gather_parser.set_defaults(func=dump)
bisect_parser = subparsers.add_parser('bisect')
bisect_parser.add_argument('-i', '--input', required=True)
bisect_parser.add_argument('cmd')
bisect_parser.set_defaults(func=bisect)
args = parser.parse_args()
args.func(args)

View File

@@ -125,6 +125,7 @@ libfreedreno_ir3_files = files(
'ir3_sched.c',
'ir3_shader.c',
'ir3_shader.h',
'ir3_shader_bisect.c',
'ir3_shared_folding.c',
'ir3_shared_ra.c',
'ir3_spill.c',