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"
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"
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"
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"
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"
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"
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"
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"
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"
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")
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)
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!")
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"
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"
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"
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