From b101aecb03e91b6ac752e1a4577fec6385913f3f Mon Sep 17 00:00:00 2001 From: Job Noorman Date: Tue, 18 Feb 2025 13:58:05 +0100 Subject: [PATCH] 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 Part-of: --- src/freedreno/ir3/ir3_compiler.c | 2 + src/freedreno/ir3/ir3_compiler.h | 6 ++ src/freedreno/ir3/ir3_compiler_nir.c | 2 + src/freedreno/ir3/ir3_disk_cache.c | 2 +- src/freedreno/ir3/ir3_shader.c | 3 +- src/freedreno/ir3/ir3_shader_bisect.c | 98 ++++++++++++++++++++++++++ src/freedreno/ir3/ir3_shader_bisect.py | 67 ++++++++++++++++++ src/freedreno/ir3/meson.build | 1 + 8 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 src/freedreno/ir3/ir3_shader_bisect.c create mode 100644 src/freedreno/ir3/ir3_shader_bisect.py diff --git a/src/freedreno/ir3/ir3_compiler.c b/src/freedreno/ir3/ir3_compiler.c index 5321f458290..2034a9d3627 100644 --- a/src/freedreno/ir3/ir3_compiler.c +++ b/src/freedreno/ir3/ir3_compiler.c @@ -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); diff --git a/src/freedreno/ir3/ir3_compiler.h b/src/freedreno/ir3/ir3_compiler.h index 1f867d3f07f..b552055c372 100644 --- a/src/freedreno/ir3/ir3_compiler.h +++ b/src/freedreno/ir3/ir3_compiler.h @@ -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_ */ diff --git a/src/freedreno/ir3/ir3_compiler_nir.c b/src/freedreno/ir3/ir3_compiler_nir.c index d9b3ec5ad60..07a060bee53 100644 --- a/src/freedreno/ir3/ir3_compiler_nir.c +++ b/src/freedreno/ir3/ir3_compiler_nir.c @@ -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); diff --git a/src/freedreno/ir3/ir3_disk_cache.c b/src/freedreno/ir3/ir3_disk_cache.c index 70126f60a76..459677207ac 100644 --- a/src/freedreno/ir3/ir3_disk_cache.c +++ b/src/freedreno/ir3/ir3_disk_cache.c @@ -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; diff --git a/src/freedreno/ir3/ir3_shader.c b/src/freedreno/ir3/ir3_shader.c index d46e78e7d65..6b8f5e3b53c 100644 --- a/src/freedreno/ir3/ir3_shader.c +++ b/src/freedreno/ir3/ir3_shader.c @@ -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); diff --git a/src/freedreno/ir3/ir3_shader_bisect.c b/src/freedreno/ir3/ir3_shader_bisect.c new file mode 100644 index 00000000000..b076c3e7794 --- /dev/null +++ b/src/freedreno/ir3/ir3_shader_bisect.c @@ -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; +} diff --git a/src/freedreno/ir3/ir3_shader_bisect.py b/src/freedreno/ir3/ir3_shader_bisect.py new file mode 100644 index 00000000000..31f6b9d0bfc --- /dev/null +++ b/src/freedreno/ir3/ir3_shader_bisect.py @@ -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) diff --git a/src/freedreno/ir3/meson.build b/src/freedreno/ir3/meson.build index dcbd14929c8..69a4c6b11e5 100644 --- a/src/freedreno/ir3/meson.build +++ b/src/freedreno/ir3/meson.build @@ -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',