def test_catchsegv_backtrace_nvvm_function_name() -> None:
    def addr2line_mock(module: Path, address: str) -> str:
        assert str(module) == "/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100"
        assert address == "0x4eab81"

        return "_nv004nvvm\n??:?\n"

    # addr2line finds function names that contain "nvvn" in NVIDIA drivers, but these
    # are useless as signatures. Thus, the offset should be used instead.

    log = """
Backtrace:
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x4eab81)[0x7f5f3434eb81]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x690978)[0x7f5f344f4978]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x695cc4)[0x7f5f344f9cc4]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x685fb4)[0x7f5f344e9fb4]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x78648a)[0x7f5f345ea48a]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x4fd52f)[0x7f5f3436152f]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x3cf602)[0x7f5f34233602]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(_nv002nvvm+0xa22)[0x7f5f3422b622]
/lib/x86_64-linux-gnu/libnvidia-glcore.so.440.100(+0x127179e)[0x7f5f3606379e]
"""
    signature = signature_util.get_signature_from_log_contents(
        log, addr2line_mock=addr2line_mock
    )
    assert signature == "libnvidiaglvkspirvso4401000x4eab81"
def test_android_hex_backtrace() -> None:
    log = """
05-23 16:56:20.744  5884  5884 F DEBUG   : backtrace:
05-23 16:56:20.745  5884  5884 F DEBUG   :   NOTE: Function names and BuildId information is missing for some frames due
05-23 16:56:20.745  5884  5884 F DEBUG   :   NOTE: to unreadable libraries. For unwinds of apps, only shared libraries
05-23 16:56:20.745  5884  5884 F DEBUG   :   NOTE: found under the lib/ directory are readable.
05-23 16:56:20.745  5884  5884 F DEBUG   :   NOTE: On this device, run setenforce 0 to make the libraries readable.
05-23 16:56:20.745  5884  5884 F DEBUG   :       #00 pc 00000000018213fc  /vendor/lib64/egl/libGLES_mali.so (BuildId: d4fb5800abef5fc4b86c0032cae223d5)
05-23 16:56:20.745  5884  5884 F DEBUG   :       #01 pc 0000000001821584  /vendor/lib64/egl/libGLES_mali.so (BuildId: d4fb5800abef5fc4b86c0032cae223d5)
05-23 16:56:20.745  5884  5884 F DEBUG   :       #02 pc 000000000182456c  /vendor/lib64/egl/libGLES_mali.so (BuildId: d4fb5800abef5fc4b86c0032cae223d5)
05-23 16:56:20.745  5884  5884 F DEBUG   :       #03 pc 0000000001821b2c  /vendor/lib64/egl/libGLES_mali.so (BuildId: d4fb5800abef5fc4b86c0032cae223d5)
05-23 16:56:20.745  5884  5884 F DEBUG   :       #04 pc 0000000000a08cac  /vendor/lib64/egl/libGLES_mali.so (BuildId: d4fb5800abef5fc4b86c0032cae223d5)
05-23 16:56:20.745  5884  5884 F DEBUG   :       #05 pc 00000000016d6ce0  /vendor/lib64/egl/libGLES_mali.so (BuildId: d4fb5800abef5fc4b86c0032cae223d5)
05-23 16:56:20.746  5884  5884 F DEBUG   :       #06 pc 00000000007de018  /vendor/lib64/egl/libGLES_mali.so (BuildId: d4fb5800abef5fc4b86c0032cae223d5)
05-23 16:56:20.746  5884  5884 F DEBUG   :       #07 pc 00000000007dcbc0  /vendor/lib64/egl/libGLES_mali.so (BuildId: d4fb5800abef5fc4b86c0032cae223d5)
05-23 16:56:20.746  5884  5884 F DEBUG   :       #08 pc 00000000000fdb4c  /data/local/tmp/amber_ndk
05-23 16:56:20.746  5884  5884 F DEBUG   :       #09 pc 00000000000d0ca0  /data/local/tmp/amber_ndk
05-23 16:56:20.746  5884  5884 F DEBUG   :       #10 pc 00000000000d1b5c  /data/local/tmp/amber_ndk
05-23 16:56:20.746  5884  5884 F DEBUG   :       #11 pc 00000000000cd8bc  /data/local/tmp/amber_ndk
05-23 16:56:20.746  5884  5884 F DEBUG   :       #12 pc 00000000000aedf0  /data/local/tmp/amber_ndk
05-23 16:56:20.746  5884  5884 F DEBUG   :       #13 pc 00000000000aeb7c  /data/local/tmp/amber_ndk
05-23 16:56:20.746  5884  5884 F DEBUG   :       #14 pc 0000000000083554  /data/local/tmp/amber_ndk
05-23 16:56:20.746  5884  5884 F DEBUG   :       #15 pc 0000000000083464  /data/local/tmp/amber_ndk
05-23 16:56:20.746  5884  5884 F DEBUG   :       #16 pc 00000000000765a8  /data/local/tmp/amber_ndk
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "00000000018213fc_egllibGLES_maliso"
Beispiel #3
0
def test_llvm_machine_code_error() -> None:
    log = """
RETURNCODE: 1
STDOUT:
ERROR: LLVM FATAL ERROR:Found 1 machine code errors.


STDERR:

# Machine code for function _amdgpu_ps_main: NoPHIs, TracksLiveness
Frame Objects:
  fi#0: size=256, align=16, at location [SP]
Function Live Ins: $sgpr2 in %37, $vgpr2 in %39, $vgpr3 in %40

bb.0..entry:
  successors: %bb.1(0x40000000), %bb.3(0x40000000); %bb.1(50.00%), %bb.3(50.00%)

# End machine code for function _amdgpu_ps_main.

*** Bad machine code: Using an undefined physical register ***
- function:    _amdgpu_ps_main
- basic block: %bb.17  (0x6f52380)
- instruction: $vcc = S_AND_B64 $exec, $vcc, implicit-def dead $scc
- operand 2:   $vcc
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "Using_an_undefined_physical_register"
Beispiel #4
0
def test_crash_offset_in_apk_lib() -> None:
    log = """
11-09 13:36:16.577 18975 18975 F DEBUG   : backtrace:
11-09 13:36:16.578 18975 18975 F DEBUG   :       #00 pc 00000000001a1234  /data/app/a.b.c-AAaa11==/base.apk!a-b.so (offset 0x123000) (!!!0000!1234abcd1234abcd!123abc!+1234) (BuildId: 1234abcde123abcd)
11-09 13:36:16.578 18975 18975 F DEBUG   :       #01 pc 00000000004cb460  /data/app/a.b.c-AAaa11==/base.apk!a-b.so (offset 0x111000) (!!!0000!aa11abc1111!11aaa!+122) (BuildId: 1234abcde123abcd)
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "abso_0x123000_00001234abcd1234abcd123abc1234"
Beispiel #5
0
def test_catchsegv_backtrace_module_not_found() -> None:
    log = """
Backtrace:
/made/up/path/install/lib64/libvulkan_intel.so(+0x76c9a4)[0x7fc67c0369a4]
/made/up/path/install/lib64/libvulkan_intel.so(+0x8ac1ef)[0x7fc67c1761ef]
    """
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "libvulkan_intelso0x76c9a4"
Beispiel #6
0
def test_catchsegv_backtrace_skip_libc() -> None:
    log = """
Backtrace:
/lib64/libc.so.6(abort+0x12b)[0x7f529082d8d9]
/made/up/path/install/lib64/libvulkan_intel.so(+0x8ac1ef)[0x7fc67c1761ef]
    """
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "libvulkan_intelso0x8ac1ef"
Beispiel #7
0
def test_catchsegv_backtrace_with_cpp_file_and_line() -> None:
    log = """

Backtrace:
/home/runner/work/gfbuild-llpc/gfbuild-llpc/vulkandriver/drivers/llvm-project/llvm/lib/CodeGen/LiveInterval.cpp:758(_ZN4llvm9LiveRange20MergeValueNumberIntoEPNS_6VNInfoES2_)[0x1202f80]
/home/runner/work/gfbuild-llpc/gfbuild-llpc/vulkandriver/drivers/llvm-project/llvm/lib/CodeGen/RegisterCoalescer.cpp:1861 (discriminator 3)(_ZN12_GLOBAL__N_117RegisterCoalescer8joinCopyEPN4llvm12MachineInstrERb)[0x13946b3]
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "LiveIntervalcpp_ZNllvmLiveRangeMergeValueNumberInt"
Beispiel #8
0
def test_catchsegv_backtrace_with_symbols() -> None:
    log = """

Backtrace:
/data/binaries/built_in/gfbuild-SPIRV-Tools-18b3b94567a9251a6f8491a6d07c4422abadd22c-Linux_x64_Debug/SPIRV-Tools/bin/spirv-opt(_ZNK8spvtools5utils17IntrusiveNodeBaseINS_3opt11InstructionEE8NextNodeEv+0x10)[0x9fa588]
/data/binaries/built_in/gfbuild-SPIRV-Tools-18b3b94567a9251a6f8491a6d07c4422abadd22c-Linux_x64_Debug/SPIRV-Tools/bin/spirv-opt(_ZNK8spvtools5utils13IntrusiveListINS_3opt11InstructionEE5emptyEv+0x1c)[0x9fa4ca]
/data/binaries/built_in/gfbuild-SPIRV-Tools-18b3b94567a9251a6f8491a6d07c4422abadd22c-Linux_x64_Debug/SPIRV-Tools/bin/spirv-opt(_ZN8spvtools3opt10BasicBlock4tailEv+0x2f)[0xa5d8ab]
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "spirvopt_ZNKspvtoolsutilsIntrusiveNodeBaseINS_optI"
Beispiel #9
0
def test_llpc_assertion_failure() -> None:
    log = """
amdllpc: /home/runner/work/gfbuild-llpc/gfbuild-llpc/vulkandriver/drivers/llvm-project/llvm/include/llvm/Support/Alignment.h:77: llvm::Align::Align(uint64_t): Assertion `Value > 0 && "Value must not be 0"' failed.
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace.
Stack dump:
0.	Program arguments: /data/binaries/built_in/gfbuild-llpc-c9c9a410565ece5c5a8735ddef7c8b24a8446db6-Linux_x64_Debug/llpc/bin/amdllpc -gfxip=9.0.0 -verify-ir -auto-layout-desc llvmAlignAlignuint_t_Assertion/9aa1fdd9_no_opt_test_amdllpc/results/amdllpc/result/variant/1_spirv/shader.frag.spv
1.	Running pass 'CallGraph Pass Manager' on module 'lgcPipeline'.
2.	Running pass 'AMDGPU DAG->DAG Pattern Instruction Selection' on function '@_amdgpu_ps_main'
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "llvmAlignAlignuint_t_Assertion"
def test_mali_error() -> None:
    log = """
--------- beginning of kernel
11-23 12:48:29.723  2507  2507 E mali 1e1000.mali: Unhandled Page fault
11-23 12:48:29.723     0     0 W         : [    C0] mali 1e1000.mali: error detected from slot 0, job status 0x00000004 (TERMINATED)
11-23 12:48:29.724   321   321 E mali 1e1000.mali: t1xx: GPU fault 0x04 from job slot 0
11-23 12:48:29.724     0     0 W         : [    C0] mali 1e1000.mali: error detected from slot 0, job status 0x00000042 (JOB_READ_FAULT)
11-23 12:48:29.724   321   321 E mali 1e1000.mali: t1xx: GPU fault 0x42 from job slot 0
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "mali_t1xx_GPU_fault_0x42_from_job_slot_0"
Beispiel #11
0
def test_llvm_fatal_error_1() -> None:
    log = """
STDOUT:
ERROR: LLVM FATAL ERROR: Broken function found, compilation aborted!


STDERR:
PHINode should have one entry for each predecessor of its parent basic block!
  %__llpc_output_proxy_.0.0 = phi float [ %.6, %163 ]
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "PHINode_should_have_one_entry_for_each_predecessor"
Beispiel #12
0
def test_llvm_fatal_error_2() -> None:
    log = """
RETURNCODE: 1
STDOUT:
ERROR: LLVM FATAL ERROR:Broken function found, compilation aborted!


STDERR:
Instruction does not dominate all uses!
  %97 = insertelement <3 x float> %96, float %.0.2, i32 2
  %92 = extractelement <3 x float> %97, i32 2
Instruction does not dominate all uses!
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "Instruction_does_not_dominate_all_uses"
def process_chunk(  # pylint: disable=too-many-locals;
    chunk_num: int, chunk: Set[str], log_files: List[Path], output_file: TextIO
) -> None:

    log(f"\nChunk {chunk_num}:")
    output_file.write(f"\nChunk {chunk_num}:\n")

    unique_signatures: Set[str] = set()

    for log_file in log_files:
        with util.file_open_text(log_file, "r") as f:
            first_line = f.readline()
            match = re.fullmatch(r"Iteration seed: (\d+)\n", first_line)
            assert match  # noqa
            seed = match.group(1)
            if seed not in chunk:
                continue

            lines = f.readlines()
            start_line = 0
            end_line = 0
            found_bug = False
            for i, line in enumerate(lines):
                match = re.fullmatch(r"STATUS (\w+)\n", line)
                if not match:
                    continue
                status = match.group(1)
                if status == "SUCCESS":
                    start_line = i + 1
                    continue
                found_bug = True
                end_line = i + 1
                break

            if not found_bug:
                continue

            failure_log = "\n".join(lines[start_line:end_line])

            signature = signature_util.get_signature_from_log_contents(failure_log)

            unique_signatures.add(signature)

    # Print the signatures.
    for signature in sorted(unique_signatures):
        log(signature)
        output_file.write(f"{signature}\n")
Beispiel #14
0
def test_asan_error() -> None:
    log = """
=================================================================
==2244339==ERROR: AddressSanitizer: use-after-poison on address 0x62500192def0 at pc 0x000006da0c70 bp 0x7ffd691028c0 sp 0x7ffd691028b8
READ of size 8 at 0x62500192def0 thread T0
    #0 0x6da0c6f in getParent /vulkandriver/drivers/llvm-project/llvm/include/llvm/CodeGen/MachineInstr.h:281:43
    #1 0x6da0c6f in llvm::LiveVariables::VarInfo::findKill(llvm::MachineBasicBlock const*) const /vulkandriver/drivers/llvm-project/llvm/lib/CodeGen/LiveVariables.cpp:62:19

0x62500192def0 is located 3568 bytes inside of 8192-byte region [0x62500192d100,0x62500192f100)
allocated by thread T0 here:
    #0 0x7f01fb89331d in operator new(unsigned long) /build/llvm-toolchain-9-uSl4bC/llvm-toolchain-9-9/projects/compiler-rt/lib/asan/asan_new_delete.cc:99:3
    #1 0x54dc395 in Allocate /vulkandriver/drivers/llvm-project/llvm/include/llvm/Support/AllocatorBase.h:85:12

SUMMARY: AddressSanitizer: use-after-poison /vulkandriver/drivers/llvm-project/llvm/include/llvm/CodeGen/MachineInstr.h:281:43 in getParent
Shadow bytes around the buggy address:
  0x0c4a8031db80: f7 00 00 00 00 00 00 00 00 f7 00 00 00 00 00 00
  0x0c4a8031db90: 00 00 f7 00 00 00 00 00 00 00 00 f7 00 00 00 00
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "useafterpoison_getParent"
def test_mesa_error_glsl_type_is_struct_or_ifc() -> None:
    log = """
*** Segmentation fault
Register dump:

 RAX: 0000000000000000   RBX: 0000000000000000   RCX: 00007fff214183c0
 RDX: 000000000000001d   RSI: 0000000001d66ce0   RDI: 0000000001e68b20
 Trap: 0000000e   Error: 00000004   OldMask: 00000000   CR2: 00000038

 XMM12: 00000000000000000000000000000000 XMM13: 00000000000000000000000000000000
 XMM14: 00000000000000000000000000000000 XMM15: 00000000000000000000000000000000

Backtrace:
/data/Mesa/mesa-20.2/lib/x86_64-linux-gnu/libvulkan_intel.so(+0x101316)[0x7f09c0dcc316]
/data/Mesa/mesa-20.2/lib/x86_64-linux-gnu/libvulkan_intel.so(+0x10467e)[0x7f09c0dcf67e]
/data/Mesa/mesa-20.2/lib/x86_64-linux-gnu/libvulkan_intel.so(+0x1073fe)[0x7f09c0dd23fe]
/home/runner/work/gfbuild-amber/gfbuild-amber/amber/b_Debug/../samples/amber.cc:605 (discriminator 2)(main)[0xc4c07f]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xea)[0x7f09c16a1cca]
/data/binaries/built_in/gfbuild-amber-760076b7c8bf43d6c6cc4928f5129afca16e895e-Linux_x64_Debug/amber/bin/amber(_start+0x29)[0xc49609]

Memory map:

00400000-01704000 r-xp 00000000 fe:01 3802475 /data/binaries/built_in/gfbuild-amber-760076b7c8bf43d6c6cc4928f5129afca16e895e-Linux_x64_Debug/amber/bin/amber
01904000-01953000 r--p 01304000 fe:01 3802475 /data/binaries/built_in/gfbuild-amber-760076b7c8bf43d6c6cc4928f5129afca16e895e-Linux_x64_Debug/amber/bin/amber
01953000-01956000 rw-p 01353000 fe:01 3802475 /data/binaries/built_in/gfbuild-amber-760076b7c8bf43d6c6cc4928f5129afca16e895e-Linux_x64_Debug/amber/bin/amber
7fff21403000-7fff21425000 rw-p 00000000 00:00 0 [stack]
7fff215a3000-7fff215a7000 r--p 00000000 00:00 0 [vvar]
7fff215a7000-7fff215a9000 r-xp 00000000 00:00 0 [vdso]


STDERR:
INTEL-MESA: warning: Performance support disabled, consider sysctl dev.i915.perf_stream_paranoid=0

SPIR-V parsing FAILED:
    In file ../src/compiler/spirv/spirv_to_nir.c:2394
    glsl_type_is_struct_or_ifc(type)
    8636 bytes into the SPIR-V binary

"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "glsl_type_is_struct_or_ifctype"
def test_catchsegv_backtrace_module_no_sig() -> None:
    def addr2line_mock(module: Path, address: str) -> str:
        assert str(module) == "/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100"
        assert address == "0x35fdfd"

        return "??\n??:0\n"

    # In this case, addr2line returns question marks, so the offset of the
    # top stack frame should be used as the signature.

    log = """
Backtrace:
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x35fdfd)[0x7f4440caedfd]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x37ac93)[0x7f4440cc9c93]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x37bbd2)[0x7f4440ccabd2]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x37c1d6)[0x7f4440ccb1d6]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(+0x37c51f)[0x7f4440ccb51f]
/lib/x86_64-linux-gnu/libnvidia-glvkspirv.so.440.100(_nv008nvvm+0xa6)[0x7f4440d10006]
"""
    signature = signature_util.get_signature_from_log_contents(
        log, addr2line_mock=addr2line_mock
    )
    assert signature == "libnvidiaglvkspirvso4401000x35fdfd"
def test_mesa_error_exec_list_length() -> None:
    log = """
*** Aborted
Register dump:

 RAX: 0000000000000000   RBX: 00007f86645f9380   RCX: 00007f866463bdb1
 RDX: 0000000000000000   RSI: 00007ffdd626b0e0   RDI: 0000000000000002
 Trap: 00000000   Error: 00000000   OldMask: 00000000   CR2: 00000000

 XMM14: 00000000000000000000000025252525 XMM15: 00000000000000000000000025252525

Backtrace:
/lib/x86_64-linux-gnu/libc.so.6(gsignal+0x141)[0x7f866463bdb1]
/lib/x86_64-linux-gnu/libc.so.6(abort+0x123)[0x7f8664625537]
/data/Mesa/mesa-20.2/lib/x86_64-linux-gnu/libvulkan_intel.so(+0x5aa709)[0x7f86641fa709]
/data/Mesa/mesa-20.2/lib/x86_64-linux-gnu/libvulkan_intel.so(+0x101953)[0x7f8663d51953]
/data/Mesa/mesa-20.2/lib/x86_64-linux-gnu/libvulkan_intel.so(+0x10467e)[0x7f8663d5467e]
STDERR:
INTEL-MESA: warning: Performance support disabled, consider sysctl dev.i915.perf_stream_paranoid=0

NIR validation failed after nir_lower_returns
2 errors:
shader: MESA_SHADER_FRAGMENT
inputs: 0
outputs: 0
uniforms: 0
ubos: 1
error: exec_list_length(&instr->srcs) == state->block->predecessors->entries (../src/compiler/nir/nir_validate.c:766)

    vec1 32 ssa_139 = deref_var &return (function_temp bool)
    vec1 1 ssa_140 = intrinsic load_deref (ssa_139) (0) /* access=0 */
    /* succs: block_18 block_19 */
1 additional errors:
error: state->ssa_srcs->entries == 0 (../src/compiler/nir/nir_validate.c:1207)
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "exec_list_lengthinstrsrcs_stateblockpredecessorsen"
def main() -> None:  # pylint: disable=too-many-locals,too-many-branches,too-many-statements;
    parser = argparse.ArgumentParser(
        description="Runs GraphicsFuzz AmberScript tests on the active devices listed in "
        "the settings.json file.",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )

    parser.add_argument(
        "--settings",
        help="Path to the settings JSON file for this instance.",
        default=str(settings_util.DEFAULT_SETTINGS_FILE_PATH),
    )

    parser.add_argument(
        "--tests",
        help="Path to the directory of AmberScript tests with shaders extracted.",
        default="graphicsfuzz",
    )

    parser.add_argument(
        "--update_ignored_signatures",
        help="As the tests are run for each device, add any crash signatures to the device's ignored_crash_signatures "
        "property and write out the updated settings.json file.",
        action="store_true",
    )

    parser.add_argument(
        "--results_out",
        help="Output file path for the CSV results table.",
        default="results.csv",
    )

    parsed_args = parser.parse_args(sys.argv[1:])

    # Args.
    tests_dir: Path = Path(parsed_args.tests)
    settings_path: Path = Path(parsed_args.settings)
    update_ignored_signatures: bool = parsed_args.update_ignored_signatures
    results_out_path: Path = Path(parsed_args.results_out)

    # Settings and devices.
    settings = settings_util.read_or_create(settings_path)
    active_devices = devices_util.get_active_devices(settings.device_list)

    # Binaries.
    binaries = binaries_util.get_default_binary_manager(settings=settings)

    work_dir = Path() / "temp" / f"cts_run_{fuzz.get_random_name()[:8]}"

    util.mkdirs_p(work_dir)

    with util.file_open_text(results_out_path, "w") as results_handle:

        def write_entry(entry: str) -> None:
            results_handle.write(entry)
            results_handle.write(", ")
            results_handle.flush()

        def write_newline() -> None:
            results_handle.write("\n")
            results_handle.flush()

        spirv_opt_path: Optional[Path] = None
        swift_shader_path: Optional[Path] = None
        amber_path: Optional[Path] = None

        # Small hack to ensure we have three devices for spirv-opt, each with a different name.
        main_spirv_opt_device: Optional[Device] = None

        if active_devices and active_devices[0].name == "host_preprocessor":
            main_spirv_opt_device = active_devices[0]
            main_spirv_opt_device.name = SPIRV_OPT_O

            spirv_opt_custom = Device()
            spirv_opt_custom.CopyFrom(main_spirv_opt_device)
            spirv_opt_custom.name = SPIRV_OPT_CUSTOM
            active_devices.insert(1, spirv_opt_custom)

            spirv_opt_os = Device()
            spirv_opt_os.CopyFrom(main_spirv_opt_device)
            spirv_opt_os.name = SPIRV_OPT_OS
            active_devices.insert(1, spirv_opt_os)

        # Enumerate active devices, writing their name and storing binary paths if needed.
        write_entry("test")
        for device in active_devices:
            write_entry(device.name)

            if device.HasField("preprocess"):
                spirv_opt_path = binaries.get_binary_path_by_name(
                    binaries_util.SPIRV_OPT_NAME
                ).path

            if device.HasField("swift_shader"):
                swift_shader_path = binaries.get_binary_path_by_name(
                    binaries_util.SWIFT_SHADER_NAME
                ).path

            if device.HasField("swift_shader") or device.HasField("host"):
                amber_path = binaries.get_binary_path_by_name(
                    binaries_util.AMBER_NAME
                ).path

        write_newline()

        # Enumerate tests and devices, writing the results.

        for test in sorted(tests_dir.glob("*.amber")):
            test_name = util.remove_end(test.name, ".amber")
            write_entry(test_name)
            spirv_shaders = sorted(
                tests_dir.glob(util.remove_end(test.name, "amber") + "*.spv")
            )
            for device in active_devices:
                test_run_dir = work_dir / f"{test_name}_{device.name}"
                util.mkdirs_p(test_run_dir)
                ignored_signatures_set: Set[str] = set(device.ignored_crash_signatures)

                with util.file_open_text(test_run_dir / "log.txt", "w") as log_stream:
                    try:
                        gflogging.push_stream_for_logging(log_stream)
                        if device.HasField("preprocess"):
                            # This just means spirv-opt for now.

                            assert spirv_opt_path  # noqa
                            assert main_spirv_opt_device  # noqa

                            # Pick spirv-opt arguments based on device name.
                            if device.name == SPIRV_OPT_O:
                                spirv_opt_args = ["-O"]
                            elif device.name == SPIRV_OPT_OS:
                                spirv_opt_args = ["-Os"]
                            elif device.name == SPIRV_OPT_CUSTOM:
                                spirv_opt_args = (
                                    spirv_opt_util.OPT_INTERESTING_SUBSET_OF_PASSES
                                )
                            else:
                                raise AssertionError(
                                    f"Can't tell how to run device {device.name}; "
                                    f"must be named host_preprocessor and be the first active device."
                                )

                            # Reset device and ignored_crash_signatures.
                            device = main_spirv_opt_device
                            ignored_signatures_set = set(
                                device.ignored_crash_signatures
                            )

                            try:
                                for spirv_shader in spirv_shaders:
                                    spirv_opt_util.run_spirv_opt_on_spirv_shader(
                                        spirv_shader,
                                        test_run_dir,
                                        spirv_opt_args,
                                        spirv_opt_path,
                                    )
                                result_util.write_status(
                                    test_run_dir, fuzz.STATUS_SUCCESS,
                                )
                            except subprocess.CalledProcessError:
                                result_util.write_status(
                                    test_run_dir, fuzz.STATUS_TOOL_CRASH,
                                )
                            except subprocess.TimeoutExpired:
                                result_util.write_status(
                                    test_run_dir, fuzz.STATUS_TOOL_TIMEOUT,
                                )
                        elif device.HasField("shader_compiler"):
                            try:
                                for spirv_shader in spirv_shaders:
                                    shader_compiler_util.run_shader(
                                        shader_compiler_device=device.shader_compiler,
                                        shader_path=spirv_shader,
                                        output_dir=test_run_dir,
                                        compiler_path=binaries.get_binary_path_by_name(
                                            device.shader_compiler.binary
                                        ).path,
                                        timeout=DEFAULT_TIMEOUT,
                                    )
                                result_util.write_status(
                                    test_run_dir, fuzz.STATUS_SUCCESS,
                                )
                            except subprocess.CalledProcessError:
                                result_util.write_status(
                                    test_run_dir, fuzz.STATUS_CRASH,
                                )
                            except subprocess.TimeoutExpired:
                                result_util.write_status(
                                    test_run_dir, fuzz.STATUS_TIMEOUT,
                                )
                        elif device.HasField("swift_shader"):
                            assert swift_shader_path  # noqa
                            assert amber_path  # noqa
                            host_device_util.run_amber(
                                test,
                                test_run_dir,
                                amber_path=amber_path,
                                dump_image=False,
                                dump_buffer=False,
                                icd=swift_shader_path,
                            )
                        elif device.HasField("host"):
                            assert amber_path  # noqa
                            host_device_util.run_amber(
                                test,
                                test_run_dir,
                                amber_path=amber_path,
                                dump_image=False,
                                dump_buffer=False,
                                custom_launcher=list(device.host.custom_launcher),
                            )
                        elif device.HasField("android"):
                            android_device.run_amber_on_device(
                                test,
                                test_run_dir,
                                dump_image=False,
                                dump_buffer=False,
                                serial=device.android.serial,
                            )
                        else:
                            raise AssertionError(f"Unsupported device {device.name}")

                    finally:
                        gflogging.pop_stream_for_logging()

                status = result_util.get_status(test_run_dir)

                if status == fuzz.STATUS_SUCCESS:
                    write_entry("P")
                elif status in (fuzz.STATUS_TIMEOUT, fuzz.STATUS_TOOL_TIMEOUT):
                    write_entry("T")
                else:
                    write_entry("F")

                # Update ignored signatures.
                if (
                    status
                    in (
                        fuzz.STATUS_TOOL_CRASH,
                        fuzz.STATUS_CRASH,
                        fuzz.STATUS_UNRESPONSIVE,
                    )
                    and update_ignored_signatures
                ):
                    log_contents = util.file_read_text(
                        result_util.get_log_path(test_run_dir)
                    )
                    signature = signature_util.get_signature_from_log_contents(
                        log_contents
                    )
                    if signature == signature_util.NO_SIGNATURE:
                        log(f"NOT updating ignored signatures to include {signature}")
                    elif signature in ignored_signatures_set:
                        log(f"Signature is already ignored: {signature}")
                    else:
                        log(f"Adding ignored signature: {signature}")
                        device.ignored_crash_signatures.append(signature)

            write_newline()

        if update_ignored_signatures:
            # Reset main_spirv_opt_device name before writing it back out.
            if main_spirv_opt_device:
                main_spirv_opt_device.name = "host_preprocessor"
            settings_util.write(settings, settings_path)
Beispiel #19
0
def main_helper(  # pylint: disable=too-many-locals,too-many-branches,too-many-statements;
    tests_dir: Path,
    work_dir: Path,
    binaries: binaries_util.BinaryManager,
    settings: Settings,
    active_devices: List[Device],
    results_out_handle: Optional[TextIO],
    updated_settings_output_path: Optional[Path],
) -> None:

    util.mkdirs_p(work_dir)

    def write_entry(entry: str) -> None:
        if not results_out_handle:
            return
        results_out_handle.write(entry)
        results_out_handle.write(", ")
        results_out_handle.flush()

    def write_newline() -> None:
        if not results_out_handle:
            return
        results_out_handle.write("\n")
        results_out_handle.flush()

    spirv_opt_path: Optional[Path] = None
    swift_shader_path: Optional[Path] = None
    amber_path: Optional[Path] = None

    # Small hack to ensure we have three devices for spirv-opt, each with a different name.
    main_spirv_opt_device: Optional[Device] = None

    if active_devices and active_devices[0].name == "host_preprocessor":
        main_spirv_opt_device = active_devices[0]
        main_spirv_opt_device.name = SPIRV_OPT_O

        spirv_opt_custom = Device()
        spirv_opt_custom.CopyFrom(main_spirv_opt_device)
        spirv_opt_custom.name = SPIRV_OPT_CUSTOM
        active_devices.insert(1, spirv_opt_custom)

        spirv_opt_os = Device()
        spirv_opt_os.CopyFrom(main_spirv_opt_device)
        spirv_opt_os.name = SPIRV_OPT_OS
        active_devices.insert(1, spirv_opt_os)

    # Enumerate active devices, writing their name and storing binary paths if needed.
    write_entry("test")
    for device in active_devices:
        write_entry(device.name)

        if device.HasField("preprocess"):
            spirv_opt_path = binaries.get_binary_path_by_name(
                binaries_util.SPIRV_OPT_NAME).path

        if device.HasField("swift_shader"):
            swift_shader_path = binaries.get_binary_path_by_name(
                binaries_util.SWIFT_SHADER_NAME).path

        if device.HasField("swift_shader") or device.HasField("host"):
            amber_path = binaries.get_binary_path_by_name(
                binaries_util.AMBER_NAME).path

    write_newline()

    # Enumerate tests and devices, writing the results.

    for test in sorted(tests_dir.glob("*.amber")):
        test_name = util.remove_end(test.name, ".amber")
        write_entry(test_name)
        spirv_shaders = sorted(
            tests_dir.glob(util.remove_end(test.name, "amber") + "*.spv"))
        for device in active_devices:
            test_run_dir = work_dir / f"{test_name}_{device.name}"
            util.mkdirs_p(test_run_dir)
            ignored_signatures_set: Set[str] = set(
                device.ignored_crash_signatures)

            with util.file_open_text(test_run_dir / "log.txt",
                                     "w") as log_stream:
                try:
                    gflogging.push_stream_for_logging(log_stream)
                    if device.HasField("preprocess"):
                        # This just means spirv-opt for now.

                        assert spirv_opt_path  # noqa
                        assert main_spirv_opt_device  # noqa

                        # Pick spirv-opt arguments based on device name.
                        if device.name == SPIRV_OPT_O:
                            spirv_opt_args = ["-O"]
                        elif device.name == SPIRV_OPT_OS:
                            spirv_opt_args = ["-Os"]
                        elif device.name == SPIRV_OPT_CUSTOM:
                            spirv_opt_args = (spirv_opt_util.
                                              OPT_INTERESTING_SUBSET_OF_PASSES)
                        else:
                            raise AssertionError(
                                f"Can't tell how to run device {device.name}; "
                                f"must be named host_preprocessor and be the first active device."
                            )

                        # Reset device and ignored_crash_signatures.
                        device = main_spirv_opt_device
                        ignored_signatures_set = set(
                            device.ignored_crash_signatures)

                        try:
                            for spirv_shader in spirv_shaders:
                                spirv_opt_util.run_spirv_opt_on_spirv_shader(
                                    spirv_shader,
                                    test_run_dir,
                                    spirv_opt_args,
                                    spirv_opt_path,
                                )
                            result_util.write_status(
                                test_run_dir,
                                fuzz.STATUS_SUCCESS,
                            )
                        except subprocess.CalledProcessError:
                            result_util.write_status(
                                test_run_dir,
                                fuzz.STATUS_TOOL_CRASH,
                            )
                        except subprocess.TimeoutExpired:
                            result_util.write_status(
                                test_run_dir,
                                fuzz.STATUS_TOOL_TIMEOUT,
                            )
                    elif device.HasField("shader_compiler"):
                        try:
                            for spirv_shader in spirv_shaders:
                                shader_compiler_util.run_shader(
                                    shader_compiler_device=device.
                                    shader_compiler,
                                    shader_path=spirv_shader,
                                    output_dir=test_run_dir,
                                    compiler_path=binaries.
                                    get_binary_path_by_name(
                                        device.shader_compiler.binary).path,
                                    timeout=DEFAULT_TIMEOUT,
                                )
                            result_util.write_status(
                                test_run_dir,
                                fuzz.STATUS_SUCCESS,
                            )
                        except subprocess.CalledProcessError:
                            result_util.write_status(
                                test_run_dir,
                                fuzz.STATUS_CRASH,
                            )
                        except subprocess.TimeoutExpired:
                            result_util.write_status(
                                test_run_dir,
                                fuzz.STATUS_TIMEOUT,
                            )
                    elif device.HasField("swift_shader"):
                        assert swift_shader_path  # noqa
                        assert amber_path  # noqa
                        host_device_util.run_amber(
                            test,
                            test_run_dir,
                            amber_path=amber_path,
                            dump_image=False,
                            dump_buffer=False,
                            icd=swift_shader_path,
                        )
                    elif device.HasField("host"):
                        assert amber_path  # noqa
                        host_device_util.run_amber(
                            test,
                            test_run_dir,
                            amber_path=amber_path,
                            dump_image=False,
                            dump_buffer=False,
                            custom_launcher=list(device.host.custom_launcher),
                        )
                    elif device.HasField("android"):
                        android_device.run_amber_on_device(
                            test,
                            test_run_dir,
                            dump_image=False,
                            dump_buffer=False,
                            serial=device.android.serial,
                        )
                    else:
                        raise AssertionError(
                            f"Unsupported device {device.name}")

                finally:
                    gflogging.pop_stream_for_logging()

            status = result_util.get_status(test_run_dir)

            if status == fuzz.STATUS_SUCCESS:
                write_entry("P")
            elif status in (fuzz.STATUS_TIMEOUT, fuzz.STATUS_TOOL_TIMEOUT):
                write_entry("T")
            else:
                write_entry("F")

            # Update ignored signatures.
            if (status in (
                    fuzz.STATUS_TOOL_CRASH,
                    fuzz.STATUS_CRASH,
                    fuzz.STATUS_UNRESPONSIVE,
            ) and updated_settings_output_path):
                log_contents = util.file_read_text(
                    result_util.get_log_path(test_run_dir))
                signature = signature_util.get_signature_from_log_contents(
                    log_contents)
                if signature == signature_util.NO_SIGNATURE:
                    log(f"NOT updating ignored signatures to include {signature}"
                        )
                elif signature in ignored_signatures_set:
                    log(f"Signature is already ignored: {signature}")
                else:
                    log(f"Adding ignored signature: {signature}")
                    device.ignored_crash_signatures.append(signature)

        write_newline()

    if updated_settings_output_path:
        # Reset main_spirv_opt_device name before writing it back out.
        if main_spirv_opt_device:
            main_spirv_opt_device.name = "host_preprocessor"
        settings_util.write(settings, updated_settings_output_path)
def main() -> None:  # pylint: disable=too-many-statements, too-many-locals, too-many-branches;
    parser = argparse.ArgumentParser(
        description="Interestingness test that runs a test using Amber, "
        "calculates the crash signature based on the result, and returns 0 "
        "if the signature matches the expected crash signature.")

    parser.add_argument(
        "source_dir",
        help=
        "The source directory containing the shaders and the test.json file that describes how to run the test.",
    )
    parser.add_argument(
        "--override_shader_job",
        nargs=2,
        metavar=("shader_job_name", "shader_job_json"),
        help=
        'Override one of the shader jobs. E.g.: "--override_shader_job variant temp/variant.json". Note that '
        "the output directory will be set to shader_job_json/ (with the .json extension removed) by default in this case. ",
    )

    parser.add_argument(
        "--override_shader",
        nargs=3,
        metavar=("shader_name", "suffix", "shader_path"),
        help=
        'Override one of the shaders. E.g.: "--override_shader variant .frag.spv temp/my_shader.spv". Note that '
        "the output directory will be set to shader_path/ (with the .spv extension removed) by default in this case. ",
    )

    parser.add_argument(
        "--use_default_binaries",
        help="Use the latest binaries, ignoring those defined in the test.json. "
        "Implies --fallback_binaries. Passing --settings is recommended to ensure the latest binaries are used.",
        action="store_true",
    )

    parser.add_argument(
        "--fallback_binaries",
        help=
        "Fallback to the latest binaries if they are not defined in the test.json.",
        action="store_true",
    )

    parser.add_argument(
        "--output",
        help=
        "Output directory. Required unless --override_shader[_job] is used; see --override_shader[_job] for details.",
        default=None,
    )

    parser.add_argument(
        "--settings",
        help="Path to a settings JSON file for this instance. "
        "Unlike with gfauto_fuzz, the default value is an empty string, which is ignored. "
        "You only need to use a settings file if you pass --use_default_binaries and you want to use the latest binary versions. "
        'In this case, use e.g. "--settings settings.json" so that a default settings file is generated with the latest binary version numbers '
        "and then run gfauto_interestingness_test again to use those latest binaries.",
        default="",
    )

    parsed_args = parser.parse_args(sys.argv[1:])

    source_dir: Path = Path(parsed_args.source_dir)
    override_shader_job: Optional[Tuple[str,
                                        str]] = parsed_args.override_shader_job
    override_shader: Optional[Tuple[str, str,
                                    str]] = parsed_args.override_shader
    settings_str: str = parsed_args.settings

    settings = Settings()
    if settings_str:
        settings = settings_util.read_or_create(Path(settings_str))

    use_default_binaries: bool = parsed_args.use_default_binaries
    fallback_binaries: bool = parsed_args.fallback_binaries or use_default_binaries
    output: Path
    if parsed_args.output:
        output = Path(parsed_args.output)
    elif override_shader_job:
        output = Path(override_shader_job[1]).with_suffix("")
    elif override_shader:
        output = Path(override_shader[2]).with_suffix("")
    else:
        raise AssertionError(
            "Need --output or --override_shader[_job] parameter.")

    binary_manager = binaries_util.get_default_binary_manager(
        settings=settings)

    if not fallback_binaries:
        binary_manager = binary_manager.get_child_binary_manager(
            binary_list=[])

    shader_job_overrides: List[tool.NameAndShaderJob] = []

    if override_shader_job:
        shader_job_overrides.append(
            tool.NameAndShaderJob(name=override_shader_job[0],
                                  shader_job=Path(override_shader_job[1])))

    shader_overrides: tool.ShaderJobNameToShaderOverridesMap = {}

    if override_shader:
        override = tool.ShaderPathWithNameAndSuffix(
            name=override_shader[0],
            suffix=override_shader[1],
            path=Path(override_shader[2]),
        )
        shader_overrides[override.name] = {override.suffix: override}

        # E.g. shader_overrides ==
        # {
        #   "variant": {
        #     ".frag.spv": ShaderPathWithNameAndSuffix("variant", ".frag.spv", Path("path/to/shader.frag.spv"))
        #   }
        # }

    # We don't need to read this to run the shader, but we need it afterwards anyway.
    test = test_util.metadata_read_from_path(source_dir /
                                             test_util.TEST_METADATA)

    output_dir = fuzz_glsl_test.run_shader_job(
        source_dir=source_dir,
        output_dir=output,
        binary_manager=binary_manager,
        test=test,
        ignore_test_and_device_binaries=use_default_binaries,
        shader_job_overrides=shader_job_overrides,
        shader_job_shader_overrides=shader_overrides,
    )

    log(f"gfauto_interestingness_test: finished running {str(source_dir)} in {str(output_dir)}."
        )

    if override_shader_job:
        log(f"The {override_shader_job[0]} shader was overridden with {override_shader_job[1]}"
            )

    status = result_util.get_status(output_dir)
    if test.expected_status:
        log("")
        log(f"Expected status: {test.expected_status}")
        log(f"Actual   status: {status}")

    log_contents = util.file_read_text(result_util.get_log_path(output_dir))
    signature = signature_util.get_signature_from_log_contents(log_contents)

    log("")
    log(f"Expected signature: {test.crash_signature}")
    log(f"Actual   signature: {signature}")

    log("")

    # The |crash_regex_override| overrides all other checks.
    if test.crash_regex_override:
        log(f"Testing crash_regex_override: {test.crash_regex_override}")
        override_pattern: Pattern[str] = re.compile(test.crash_regex_override,
                                                    re.DOTALL)
        match: Optional[Match[str]] = override_pattern.fullmatch(log_contents)
        if match:
            log("Match!")
            log("Interesting")
            sys.exit(0)
        else:
            log("No match; not interesting")
            sys.exit(1)

    if test.expected_status:
        if status != test.expected_status:
            log("status != expected_status; not interesting")
            sys.exit(1)
    else:
        # There is no expected status given, so just assume it needs to be one of the "bad" statuses:
        if status not in (
                fuzz.STATUS_CRASH,
                fuzz.STATUS_TOOL_CRASH,
                fuzz.STATUS_UNRESPONSIVE,
        ):
            log("shader run did not fail; not interesting.")
            sys.exit(1)

    if signature != test.crash_signature:
        log("signature != crash_signature; not interesting")
        sys.exit(1)

    log("Interesting!")
Beispiel #21
0
def test_glslang_error_2() -> None:
    log = """
ERROR: reports/.../part_1_preserve_semantics/reduction_work/variant/shader_reduced_0173/0_glsl/shader_reduced_0173.frag:456: '=' :  cannot convert from ' const 3-component vector of bool' to ' temp bool'
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "cannot_convert_from_const_component_vector_of_bool"
Beispiel #22
0
def test_glslang_error_1() -> None:
    log = """
ERROR: temp/.../variant/shader.frag:549: 'variable indexing fragment shader output array' : not supported with this profile: es
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "variable_indexing_fragment_shader_output_array_not"
Beispiel #23
0
def test_glslang_assertion() -> None:
    log = """
glslangValidator: ../glslang/MachineIndependent/ParseHelper.cpp:2212: void glslang::TParseContext::nonOpBuiltInCheck(const glslang::TSourceLoc&, const glslang::TFunction&, glslang::TIntermAggregate&): Assertion `PureOperatorBuiltins == false' failed.
"""
    signature = signature_util.get_signature_from_log_contents(log)
    assert signature == "void_glslangTParseContextnonOpBuiltInCheckconst_gl"
Beispiel #24
0
def maybe_add_report(  # pylint: disable=too-many-locals;
        test_dir: Path, reports_dir: Path, device: Device,
        settings: Settings) -> Optional[Path]:

    result_output_dir = test_util.get_results_directory(test_dir, device.name)

    status = result_util.get_status(result_output_dir)

    report_subdirectory_name = ""

    if status == fuzz.STATUS_CRASH:
        report_subdirectory_name = "crashes"
    elif status == fuzz.STATUS_TOOL_CRASH:
        report_subdirectory_name = "tool_crashes"
    elif status == fuzz.STATUS_UNRESPONSIVE:
        report_subdirectory_name = "unresponsive"

    if not report_subdirectory_name:
        return None
    log_path = result_util.get_log_path(result_output_dir)

    log_contents = util.file_read_text(log_path)
    signature = signature_util.get_signature_from_log_contents(log_contents)

    signature_dir = reports_dir / report_subdirectory_name / signature

    util.mkdirs_p(signature_dir)

    # If the signature_dir contains a NOT_INTERESTING file, then don't bother creating a report.
    if (signature_dir / "NOT_INTERESTING").exists():
        return None

    if signature != signature_util.BAD_IMAGE_SIGNATURE:
        # If we have reached the maximum number of crashes per signature for this device, don't create a report.
        num_duplicates = [
            report_dir for report_dir in signature_dir.iterdir() if
            report_dir.is_dir() and report_dir.name.endswith(f"_{device.name}")
        ]
        if len(num_duplicates) >= settings.maximum_duplicate_crashes:
            return None

    # We include the device name in the directory name because it is possible that this test crashes on two
    # different devices but gives the same crash signature in both cases (e.g. for generic signatures
    # like "compile_error"). This would lead to two test copies having the same path.
    # It also means we can limit duplicates per device using the directory name.
    test_dir_in_reports = signature_dir / f"{test_dir.name}_{device.name}"

    util.copy_dir(test_dir, test_dir_in_reports)

    if signature != signature_util.BAD_IMAGE_SIGNATURE:

        # If we found a crash, rename the directories for all shaders other than the variant. Thus, only the variant
        # shader will run.

        bad_shader_name = result_util.get_status_bad_shader_name(
            test_util.get_results_directory(test_dir_in_reports, device.name))

        # TODO: Could possibly improve this. Could try scanning the Amber log to figure out which shader failed?

        if not bad_shader_name:
            log("WARNING: assuming that the bad shader is the variant")
            bad_shader_name = test_util.VARIANT_DIR

        shader_jobs = tool.get_shader_jobs(
            test_util.get_source_dir(test_dir_in_reports))
        found_bad_shader = False
        for shader_job in shader_jobs:
            if shader_job.name == bad_shader_name:
                found_bad_shader = True
            else:
                shader_job.shader_job.parent.rename(
                    shader_job.shader_job.parent.parent /
                    f"_{shader_job.name}")
        check(
            found_bad_shader,
            AssertionError(
                f"Could not find bad shader at: {test_util.get_source_dir(test_dir_in_reports) / bad_shader_name}"
            ),
        )

    test_metadata = test_util.metadata_read(test_dir_in_reports)
    test_metadata.crash_signature = signature
    test_metadata.device.CopyFrom(device)
    test_metadata.expected_status = status
    test_util.metadata_write(test_metadata, test_dir_in_reports)

    return test_dir_in_reports