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:
@@ -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);
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
98
src/freedreno/ir3/ir3_shader_bisect.c
Normal file
98
src/freedreno/ir3/ir3_shader_bisect.c
Normal 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;
|
||||
}
|
||||
67
src/freedreno/ir3/ir3_shader_bisect.py
Normal file
67
src/freedreno/ir3/ir3_shader_bisect.py
Normal 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)
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user