def ensure_amber_installed( device_serial: Optional[str], binary_manager: binaries_util.BinaryManager ) -> None: amber_apk_binary = binary_manager.get_binary_path_by_name("amber_apk") amber_apk_test_binary = binary_manager.get_binary_path_by_name("amber_apk_test") adb_can_fail(device_serial, ["uninstall", "com.google.amber"]) adb_can_fail(device_serial, ["uninstall", "com.google.amber.test"]) adb_check(device_serial, ["install", "-r", str(amber_apk_binary.path)]) adb_check(device_serial, ["install", "-r", str(amber_apk_test_binary.path)])
def add_spirv_shader_test_binaries( test: Test, spirv_opt_args: Optional[List[str]], binary_manager: binaries_util.BinaryManager, ) -> Test: test.binaries.extend([binary_manager.get_binary_by_name(name="spirv-dis")]) test.binaries.extend([binary_manager.get_binary_by_name(name="spirv-val")]) if spirv_opt_args: test.binaries.extend([binary_manager.get_binary_by_name(name="spirv-opt")]) test.binaries.extend([binary_manager.get_binary_by_name(name="amber")]) test.binaries.extend([binary_manager.get_binary_by_name(name="amber_apk")]) test.binaries.extend([binary_manager.get_binary_by_name(name="amber_apk_test")]) return test
def run_shader_job( shader_compiler_device: DeviceShaderCompiler, spirv_shader_job_path: Path, output_dir: Path, binary_manager: binaries_util.BinaryManager, ) -> List[Path]: compiler_path = binary_manager.get_binary_path_by_name( shader_compiler_device.binary).path log(f"Running {str(compiler_path)} on shader job {str(spirv_shader_job_path)}" ) shader_paths = shader_job_util.get_related_files( spirv_shader_job_path, language_suffix=[shader_job_util.SUFFIX_SPIRV]) log(f"Running {str(compiler_path)} on shaders: {shader_paths}") result = [] for shader_path in shader_paths: result.append( run_shader( shader_compiler_device, compiler_path=compiler_path, shader_path=shader_path, output_dir=output_dir, )) return result
def ensure_amber_installed( device_serial: Optional[str], binary_manager: binaries_util.BinaryManager) -> None: amber_apk_binary = binary_manager.get_binary_path_by_name("amber_apk") amber_apk_test_binary = binary_manager.get_binary_path_by_name( "amber_apk_test") adb_can_fail(device_serial, ["uninstall", "com.google.amber"]) adb_can_fail(device_serial, ["uninstall", "com.google.amber.test"]) adb_check(device_serial, ["install", "-r", str(amber_apk_binary.path)]) adb_check( device_serial, ["install", "-r", str(amber_apk_test_binary.path)]) # Run Amber once to ensure the external cache directory (on /sdcard) gets created by the application. adb_check(device_serial, ["shell", get_amber_adb_shell_cmd(["-d"])], verbose=True)
def device_host(binary_manager: binaries_util.BinaryManager) -> Device: amber_path = binary_manager.get_binary_path_by_name(binaries_util.AMBER_NAME).path driver_details = "" try: driver_details = host_device_util.get_driver_details(amber_path) except GetDeviceDetailsError as ex: log(f"WARNING: Failed to get device driver details: {ex}") return Device(name="host", host=DeviceHost(), device_properties=driver_details)
def make_test( base_source_dir: Path, subtest_dir: Path, spirv_opt_args: Optional[List[str]], binary_manager: binaries_util.BinaryManager, ) -> Path: # Create the subtest by copying the base source. util.copy_dir(base_source_dir, test_util.get_source_dir(subtest_dir)) test = Test(spirv_fuzz=TestSpirvFuzz(spirv_opt_args=spirv_opt_args)) test.binaries.extend([binary_manager.get_binary_by_name(name="spirv-dis")]) test.binaries.extend([binary_manager.get_binary_by_name(name="spirv-val")]) if spirv_opt_args: test.binaries.extend( [binary_manager.get_binary_by_name(name="spirv-opt")]) # Write the test metadata. test_util.metadata_write(test, subtest_dir) return subtest_dir
def run_glsl_reduce( source_dir: Path, name_of_shader_to_reduce: str, output_dir: Path, binary_manager: binaries_util.BinaryManager, preserve_semantics: bool = False, extra_args: Optional[List[str]] = None, ) -> Path: input_shader_job = source_dir / name_of_shader_to_reduce / test_util.SHADER_JOB glsl_reduce_path = util.tool_on_path( "glsl-reduce", str( binary_manager.get_binary_path_by_name( "graphicsfuzz-tool").path.parent), ) cmd = [ str(glsl_reduce_path), str(input_shader_job), "--output", str(output_dir), ] if preserve_semantics: cmd.append("--preserve-semantics") if extra_args: cmd.extend(extra_args) cmd.extend([ # This ensures the arguments that follow are all positional arguments. "--", "gfauto_interestingness_test", str(source_dir), # --override_shader_job requires two parameters to follow; the second will be added by glsl-reduce (the shader.json file). "--override_shader_job", str(name_of_shader_to_reduce), ]) # Log the reduction. with util.file_open_text(output_dir / "command.log", "w") as f: gflogging.push_stream_for_logging(f) try: # The reducer can fail, but it will typically output an exception file, so we can ignore the exit code. subprocess_util.run(cmd, verbose=True, check_exit_code=False) finally: gflogging.pop_stream_for_logging() return output_dir
def swift_shader_device(binary_manager: binaries_util.BinaryManager) -> Device: amber_path = binary_manager.get_binary_path_by_name(binaries_util.AMBER_NAME).path swift_shader_binary_and_path = binary_manager.get_binary_path_by_name( binaries_util.SWIFT_SHADER_NAME ) driver_details = "" try: driver_details = host_device_util.get_driver_details( amber_path, swift_shader_binary_and_path.path ) except GetDeviceDetailsError as ex: log(f"WARNING: Failed to get device driver details: {ex}") device = Device( name="swift_shader", swift_shader=DeviceSwiftShader(), binaries=[swift_shader_binary_and_path.binary], device_properties=driver_details, ) return device
def get_device_list( binary_manager: binaries_util.BinaryManager, device_list: Optional[DeviceList] = None, ) -> DeviceList: if not device_list: device_list = DeviceList() # We use |extend| below (instead of |append|) because you cannot append to a list of non-scalars in protobuf. # |extend| copies the elements from the list and appends them. # Host preprocessor. device = device_preprocessor() device_list.devices.extend([device]) device_list.active_device_names.append(device.name) # SwiftShader. device = swift_shader_device(binary_manager) device_list.devices.extend([device]) device_list.active_device_names.append(device.name) # Host device. device = device_host(binary_manager) device_list.devices.extend([device]) device_list.active_device_names.append(device.name) try: # Android devices. android_devices = android_device.get_all_android_devices(binary_manager) device_list.devices.extend(android_devices) device_list.active_device_names.extend([d.name for d in android_devices]) except ToolNotOnPathError: log( "WARNING: adb was not found on PATH nor was ANDROID_HOME set; " "Android devices will not be added to settings.json" ) # Offline compiler. device = Device( name="amdllpc", shader_compiler=DeviceShaderCompiler( binary="amdllpc", args=["-gfxip=9.0.0", "-verify-ir", "-auto-layout-desc"] ), binaries=[binary_manager.get_binary_path_by_name("amdllpc").binary], ) device_list.devices.extend([device]) # Don't add to active devices, since this is mostly just an example. return device_list
def _update_device_swiftshader(binary_manager: binaries_util.BinaryManager, device: Device) -> None: check( device.HasField("swift_shader"), AssertionError(f"Expected SwiftShader device: {device}"), ) amber_path = binary_manager.get_binary_path_by_name( binaries_util.AMBER_NAME).path swift_shader_binary_and_path = binary_manager.get_binary_path_by_name( binaries_util.SWIFT_SHADER_NAME) driver_details = "" try: driver_details = host_device_util.get_driver_details( amber_path, swift_shader_binary_and_path.path) except GetDeviceDetailsError as ex: log(f"WARNING: Failed to get device driver details: {ex}") device.device_properties = driver_details del device.binaries[:] device.binaries.extend([swift_shader_binary_and_path.binary])
def _update_device_host(binary_manager: binaries_util.BinaryManager, device: Device) -> None: check( device.HasField("host"), AssertionError(f"Expected host device: {device}"), ) amber_path = binary_manager.get_binary_path_by_name( binaries_util.AMBER_NAME).path driver_details = "" try: driver_details = host_device_util.get_driver_details( amber_path, custom_launcher=list(device.host.custom_launcher)) except GetDeviceDetailsError as ex: log(f"WARNING: Failed to get device driver details: {ex}") device.device_properties = driver_details
def make_test( base_source_dir: Path, subtest_dir: Path, spirv_opt_args: Optional[List[str]], binary_manager: binaries_util.BinaryManager, ) -> Path: # Create the subtest by copying the base source. util.copy_dir(base_source_dir, test_util.get_source_dir(subtest_dir)) test = Test(glsl=TestGlsl(spirv_opt_args=spirv_opt_args)) test.binaries.extend( [binary_manager.get_binary_by_name(name="glslangValidator")]) add_spirv_shader_test_binaries(test, spirv_opt_args, binary_manager) # Write the test metadata. test_util.metadata_write(test, subtest_dir) return subtest_dir
def make_test( base_source_dir: Path, subtest_dir: Path, spirv_opt_args: Optional[List[str]], binary_manager: binaries_util.BinaryManager, derived_from: Optional[str], stable_shader: bool, common_spirv_args: Optional[List[str]], ) -> Path: source_dir = test_util.get_source_dir(subtest_dir) # Create the subtest by copying the base source. util.copy_dir(base_source_dir, source_dir) test = Test( glsl=TestGlsl(spirv_opt_args=spirv_opt_args), derived_from=derived_from, common_spirv_args=common_spirv_args, ) test.binaries.extend( [binary_manager.get_binary_by_name(name="glslangValidator")]) fuzz_test_util.add_spirv_shader_test_binaries(test, spirv_opt_args, binary_manager) # Write the test metadata. test_util.metadata_write(test, subtest_dir) # If the reference shader is "stable" (with respect to floating-point sensitivity) # then this can be a wrong image test; i.e. we will render the reference and variant shaders # and compare the output images. # Otherwise, we should just render the variant shader and check for crashes; to do this, # we just rename the `reference/` directory to `_reference/` so that the test has no reference shader. if not stable_shader and (source_dir / test_util.REFERENCE_DIR).is_dir(): util.move_dir( source_dir / test_util.REFERENCE_DIR, source_dir / f"_{test_util.REFERENCE_DIR}", ) return subtest_dir
def _update_device_shader_compiler(binary_manager: binaries_util.BinaryManager, device: Device) -> None: check( device.HasField("shader_compiler"), AssertionError(f"Expected shader_compiler device: {device}"), ) # The only thing we can do is update the shader compiler binary if it is a built-in binary. if binaries_util.is_built_in_binary_name(device.shader_compiler.binary): # Remove existing binaries with this name from the device's binaries list. binaries = list(device.binaries) binaries = [ b for b in binaries if b.name != device.shader_compiler.binary ] del device.binaries[:] device.binaries.extend(binaries) # Add our latest version of the binary. device.binaries.extend( [binary_manager.get_binary_by_name(device.shader_compiler.binary)])
def create_spirv_fuzz_variant_2( source_dir: Path, binary_manager: binaries_util.BinaryManager, settings: Settings, ) -> Optional[Path]: """ Replays all transformations except the last to get variant_2. Replays all transformations except the last to get a variant_2 shader job, such that variant <-> variant_2 are likely even more similar than reference <-> variant. |source_dir| must be a spirv_fuzz test. """ test_metadata: Test = test_util.metadata_read_from_source_dir(source_dir) check(test_metadata.HasField("spirv_fuzz"), AssertionError("Not a spirv_fuzz test")) variant_shader_job = source_dir / test_util.VARIANT_DIR / test_util.SHADER_JOB variant_2_shader_job = ( source_dir / f"{test_util.VARIANT_DIR}_2" / test_util.SHADER_JOB ) if not variant_shader_job.is_file(): log( f"Skip generating variant_2 for {str(source_dir)} because the variant shader job was not found." ) return None if variant_2_shader_job.is_file(): log( f"Skip generating variant_2 for {str(source_dir)} because variant_2 shader job already exists." ) return None return spirv_fuzz_util.run_replay_on_shader_job( spirv_fuzz_path=binary_manager.get_binary_path_by_name( binaries_util.SPIRV_FUZZ_NAME ).path, variant_shader_job_json=variant_shader_job, output_shader_job_json=variant_2_shader_job, other_args=list(settings.common_spirv_args), )
def run_shader_job( # pylint: disable=too-many-return-statements,too-many-branches, too-many-locals, too-many-statements; source_dir: Path, output_dir: Path, binary_manager: binaries_util.BinaryManager, test: Optional[Test] = None, device: Optional[Device] = None, ignore_test_and_device_binaries: bool = False, shader_job_overrides: Iterable[tool.NameAndShaderJob] = (), shader_job_shader_overrides: Optional[ tool.ShaderJobNameToShaderOverridesMap] = None, ) -> Path: if not shader_job_shader_overrides: shader_job_shader_overrides = {} with util.file_open_text(output_dir / "log.txt", "w") as log_file: try: gflogging.push_stream_for_logging(log_file) # TODO: Find amber path. NDK or host. # TODO: If Amber is going to be used, check if Amber can use Vulkan debug layers now, and if not, pass that # info down via a bool. if not test: test = test_util.metadata_read_from_path( source_dir / test_util.TEST_METADATA) if not device: device = test.device log(f"Running test on device:\n{device.name}") # We will create a binary_manager child with a restricted set of binaries so that we only use the binaries # specified in the test and by the device; if some required binaries are not specified by the test nor the # device, there will be an error instead of falling back to our default binaries. But we keep a reference to # the parent so we can still access certain "test-independent" binaries like Amber. binary_manager_parent = binary_manager if not ignore_test_and_device_binaries: binary_manager = binary_manager.get_child_binary_manager( list(device.binaries) + list(test.binaries)) spirv_opt_hash: Optional[str] = None spirv_opt_args: Optional[List[str]] = None if test.glsl.spirv_opt_args or test.spirv_fuzz.spirv_opt_args: spirv_opt_hash = binary_manager.get_binary_by_name( binaries_util.SPIRV_OPT_NAME).version spirv_opt_args = (list(test.glsl.spirv_opt_args) if test.glsl.spirv_opt_args else list( test.spirv_fuzz.spirv_opt_args)) shader_jobs = tool.get_shader_jobs(source_dir, overrides=shader_job_overrides) combined_spirv_shader_jobs: List[tool.SpirvCombinedShaderJob] = [] for shader_job in shader_jobs: try: shader_overrides = shader_job_shader_overrides.get( shader_job.name, None) combined_spirv_shader_jobs.append( tool.compile_shader_job( name=shader_job.name, input_json=shader_job.shader_job, work_dir=output_dir / shader_job.name, binary_paths=binary_manager, spirv_opt_args=spirv_opt_args, shader_overrides=shader_overrides, )) except subprocess.CalledProcessError: result_util.write_status(output_dir, fuzz.STATUS_TOOL_CRASH, shader_job.name) return output_dir except subprocess.TimeoutExpired: result_util.write_status(output_dir, fuzz.STATUS_TOOL_TIMEOUT, shader_job.name) return output_dir # Device types: |preprocess| and |shader_compiler| don't need an AmberScript file. # noinspection PyTypeChecker if device.HasField("preprocess"): # The "preprocess" device type just needs to get this far, so this is a success. result_util.write_status(output_dir, fuzz.STATUS_SUCCESS) return output_dir # noinspection PyTypeChecker if device.HasField("shader_compiler"): for combined_spirv_shader_job in combined_spirv_shader_jobs: try: shader_compiler_util.run_shader_job( device.shader_compiler, combined_spirv_shader_job.spirv_shader_job, output_dir, binary_manager=binary_manager, ) except subprocess.CalledProcessError: result_util.write_status( output_dir, fuzz.STATUS_CRASH, combined_spirv_shader_job.name, ) return output_dir except subprocess.TimeoutExpired: result_util.write_status( output_dir, fuzz.STATUS_TIMEOUT, combined_spirv_shader_job.name, ) return output_dir # The shader compiler succeeded on all files; this is a success. result_util.write_status(output_dir, fuzz.STATUS_SUCCESS) return output_dir # Other device types need an AmberScript file. amber_converter_shader_job_files = [ amber_converter.ShaderJobFile( name_prefix=combined_spirv_shader_job.name, asm_spirv_shader_job_json=combined_spirv_shader_job. spirv_asm_shader_job, glsl_source_json=combined_spirv_shader_job. glsl_source_shader_job, processing_info="", ) for combined_spirv_shader_job in combined_spirv_shader_jobs ] # Check if the first is the reference shader; if so, pull it out into its own variable. reference: Optional[amber_converter.ShaderJobFile] = None variants = amber_converter_shader_job_files if (amber_converter_shader_job_files[0].name_prefix == test_util.REFERENCE_DIR): reference = amber_converter_shader_job_files[0] variants = variants[1:] elif len(variants) > 1: raise AssertionError( "More than one variant, but no reference. This is unexpected." ) amber_script_file = amber_converter.spirv_asm_shader_job_to_amber_script( shader_job_file_amber_test=amber_converter. ShaderJobFileBasedAmberTest(reference_asm_spirv_job=reference, variants_asm_spirv_job=variants), output_amber_script_file_path=output_dir / "test.amber", amberfy_settings=amber_converter.AmberfySettings( spirv_opt_args=spirv_opt_args, spirv_opt_hash=spirv_opt_hash), ) is_compute = bool( shader_job_util.get_related_files( combined_spirv_shader_jobs[0].spirv_shader_job, [shader_job_util.EXT_COMP], )) # noinspection PyTypeChecker if device.HasField("host") or device.HasField("swift_shader"): icd: Optional[Path] = None # noinspection PyTypeChecker if device.HasField("swift_shader"): icd = binary_manager.get_binary_path_by_name( binaries_util.SWIFT_SHADER_NAME).path # Run the test on the host using Amber. host_device_util.run_amber( amber_script_file, output_dir, amber_path=binary_manager_parent.get_binary_path_by_name( binaries_util.AMBER_NAME).path, dump_image=(not is_compute), dump_buffer=is_compute, icd=icd, ) return output_dir # noinspection PyTypeChecker if device.HasField("android"): android_device.run_amber_on_device( amber_script_file, output_dir, dump_image=(not is_compute), dump_buffer=is_compute, serial=device.android.serial, ) return output_dir # TODO: For a remote device (which we will probably need to support), use log_a_file to output the # "amber_log.txt" file. raise AssertionError(f"Unhandled device type:\n{str(device)}") finally: gflogging.pop_stream_for_logging()
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 write_shader( shader_asm: str, amber_file: Path, output_dir: Path, shader_type: str, shader_name: str, binaries: binaries_util.BinaryManager, ) -> List[Path]: files_written: List[Path] = [] shader_type_to_suffix = { "fragment": shader_job_util.EXT_FRAG, "vertex": shader_job_util.EXT_VERT, "compute": shader_job_util.EXT_COMP, } shader_type_suffix = shader_type_to_suffix[shader_type] # E.g. ifs-and-whiles.variant_fragment_shader.frag.asm shader_asm_file_path = output_dir / ( f"{amber_file.stem}.{shader_name}{shader_type_suffix}{shader_job_util.SUFFIX_ASM_SPIRV}" ) # E.g. ifs-and-whiles.variant_fragment_shader.frag.spv shader_spirv_file_path = output_dir / ( f"{amber_file.stem}.{shader_name}{shader_type_suffix}{shader_job_util.SUFFIX_SPIRV}" ) # E.g. dEQP-VK.graphicsfuzz.ifs-and-whiles.variant_fragment_shader.spvas # These files can be added to the llpc repo as a shader test. shader_llpc_asm_test_file_path = output_dir / ( f"dEQP-VK.graphicsfuzz.{amber_file.stem}.{shader_name}.spvas") util.file_write_text(shader_asm_file_path, shader_asm) files_written.append(shader_asm_file_path) spirv_as_path = binaries.get_binary_path_by_name("spirv-as").path subprocess_util.run( [ str(spirv_as_path), "-o", str(shader_spirv_file_path), str(shader_asm_file_path), "--target-env", "spv1.0", ], verbose=True, ) files_written.append(shader_spirv_file_path) util.file_write_text( shader_llpc_asm_test_file_path, """; BEGIN_SHADERTEST ; RUN: amdllpc -verify-ir -spvgen-dir=%spvgendir% -v %gfxip %s | FileCheck -check-prefix=SHADERTEST %s ; SHADERTEST-LABEL: {{^// LLPC.*}} SPIRV-to-LLVM translation results ; SHADERTEST: AMDLLPC SUCCESS ; END_SHADERTEST ; """ + f"; Based on dEQP-VK.graphicsfuzz.{amber_file.stem}\n\n" + shader_asm, ) files_written.append(shader_llpc_asm_test_file_path) return files_written
def fuzz_glsl( # pylint: disable=too-many-locals; staging_dir: Path, reports_dir: Path, fuzz_failures_dir: Path, active_devices: List[Device], references: List[Path], donors_dir: Path, settings: Settings, binary_manager: binaries_util.BinaryManager, ) -> None: staging_name = staging_dir.name template_source_dir = staging_dir / "source_template" # Pick a randomly chosen reference. unprepared_reference_shader_job: Path = random.choice(references) # The "graphicsfuzz-tool" tool is designed to be on your PATH so that e.g. ".bat" will be appended on Windows. # So we use tool_on_path with a custom PATH to get the actual file we want to execute. graphicsfuzz_tool_path = util.tool_on_path( "graphicsfuzz-tool", str( binary_manager.get_binary_path_by_name( "graphicsfuzz-tool").path.parent), ) try: with util.file_open_text(staging_dir / "log.txt", "w") as log_file: try: gflogging.push_stream_for_logging(log_file) # Create the prepared (for Vulkan GLSL) reference. glsl_generate_util.run_prepare_reference( graphicsfuzz_tool_path, unprepared_reference_shader_job, template_source_dir / test_util.REFERENCE_DIR / test_util.SHADER_JOB, legacy_graphics_fuzz_vulkan_arg=settings. legacy_graphics_fuzz_vulkan_arg, ) # Generate the variant (GraphicsFuzz requires the unprepared reference as input). glsl_generate_util.run_generate( graphicsfuzz_tool_path, unprepared_reference_shader_job, donors_dir, template_source_dir / test_util.VARIANT_DIR / test_util.SHADER_JOB, seed=str( random.getrandbits( glsl_generate_util.GENERATE_SEED_BITS)), other_args=list(settings.extra_graphics_fuzz_generate_args) if settings.extra_graphics_fuzz_generate_args else None, legacy_graphics_fuzz_vulkan_arg=settings. legacy_graphics_fuzz_vulkan_arg, ) finally: gflogging.pop_stream_for_logging() except subprocess.CalledProcessError: util.mkdirs_p(fuzz_failures_dir) if len(list( fuzz_failures_dir.iterdir())) < settings.maximum_fuzz_failures: util.copy_dir(staging_dir, fuzz_failures_dir / staging_dir.name) return reference_name = unprepared_reference_shader_job.stem stable_shader = reference_name.startswith("stable_") common_spirv_args = list(settings.common_spirv_args) test_dirs = [ make_test( template_source_dir, staging_dir / f"{staging_name}_no_opt_test", spirv_opt_args=None, binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), make_test( template_source_dir, staging_dir / f"{staging_name}_opt_O_test", spirv_opt_args=["-O"], binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), ] if not settings.spirv_opt_just_o: test_dirs += [ make_test( template_source_dir, staging_dir / f"{staging_name}_opt_Os_test", spirv_opt_args=["-Os"], binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), make_test( template_source_dir, staging_dir / f"{staging_name}_opt_rand1_test", spirv_opt_args=spirv_opt_util.random_spirv_opt_args(), binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), make_test( template_source_dir, staging_dir / f"{staging_name}_opt_rand2_test", spirv_opt_args=spirv_opt_util.random_spirv_opt_args(), binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), make_test( template_source_dir, staging_dir / f"{staging_name}_opt_rand3_test", spirv_opt_args=spirv_opt_util.random_spirv_opt_args(), binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), ] for test_dir in test_dirs: interrupt_util.interrupt_if_needed() if handle_test(test_dir, reports_dir, active_devices, binary_manager, settings): # If we generated a report, don't bother trying other optimization combinations. break
def create_summary_and_reproduce( # pylint: disable=too-many-locals; test_dir: Path, binary_manager: binaries_util.BinaryManager, settings: Settings) -> None: test_metadata = test_util.metadata_read(test_dir) summary_dir = test_dir / "summary" summary_source_dirs: List[Path] = [] unreduced = util.copy_dir(test_util.get_source_dir(test_dir), summary_dir / "unreduced") summary_source_dirs.append(unreduced) # For the `summary/reduced_1/` directory. reduction_output_dir_1 = test_util.get_reduced_test_dir( test_dir, test_metadata.device.name, "1") reduced_1: Optional[Path] = None if reduction_output_dir_1.is_dir(): reduction_output_source_dir_1 = test_util.get_source_dir( reduction_output_dir_1) if reduction_output_source_dir_1.is_dir(): reduced_1 = util.copy_dir(reduction_output_source_dir_1, summary_dir / "reduced_1") summary_source_dirs.append(reduced_1) # For the `summary/reduced_2/` directory. reduction_output_dir_2 = test_util.get_reduced_test_dir( test_dir, test_metadata.device.name, "2") if reduction_output_dir_2.is_dir(): reduction_output_source_dir_2 = test_util.get_source_dir( reduction_output_dir_2) if reduction_output_source_dir_2.is_dir(): reduced_2 = util.copy_dir(reduction_output_source_dir_2, summary_dir / "reduced_2") summary_source_dirs.append(reduced_2) # If this test was generated from a stable shader... if test_metadata.derived_from.startswith("stable_") and reduced_1: # Before running the reduced_1 source dir, find any renamed shader jobs (e.g. reference/ -> _reference/) # and rename them back. Thus, the modified test becomes a wrong image test once again, even though # the actual bug was probably a crash bug. renamed_shader_jobs = list(reduced_1.glob("_*")) renamed_shader_jobs = [ r for r in renamed_shader_jobs if (r / test_util.SHADER_JOB).is_file() ] if renamed_shader_jobs: for renamed_shader_job in renamed_shader_jobs: util.move_dir( renamed_shader_job, renamed_shader_job.with_name(renamed_shader_job.name[1:]), ) # Also, if this is a spirv_fuzz test then try to create a variant_2 shader job that is even more similar to the # variant than the reference shader job. if test_metadata.HasField("spirv_fuzz"): spirv_fuzz_util.create_spirv_fuzz_variant_2( spirv_fuzz_path=binary_manager.get_binary_path_by_name( binaries_util.SPIRV_FUZZ_NAME).path, source_dir=reduced_1, settings=settings, ) # Run every source dir that we added to the summary dir. for summary_source_dir in summary_source_dirs: run_shader_job( source_dir=summary_source_dir, output_dir=(summary_dir / f"{summary_source_dir.name}_result"), binary_manager=binary_manager, )
def fuzz_spirv( # pylint: disable=too-many-locals; staging_dir: Path, reports_dir: Path, fuzz_failures_dir: Path, active_devices: List[Device], spirv_fuzz_shaders: List[Path], settings: Settings, binary_manager: binaries_util.BinaryManager, ) -> None: staging_name = staging_dir.name template_source_dir = staging_dir / "source_template" reference_spirv_shader_job_orig_path: Path = random.choice( spirv_fuzz_shaders) # Copy in a randomly chosen reference. reference_spirv_shader_job = shader_job_util.copy( reference_spirv_shader_job_orig_path, template_source_dir / test_util.REFERENCE_DIR / test_util.SHADER_JOB, language_suffix=shader_job_util.SUFFIXES_SPIRV_FUZZ_INPUT, ) try: with util.file_open_text(staging_dir / "log.txt", "w") as log_file: try: gflogging.push_stream_for_logging(log_file) spirv_fuzz_util.run_generate_on_shader_job( binary_manager.get_binary_path_by_name("spirv-fuzz").path, reference_spirv_shader_job, template_source_dir / test_util.VARIANT_DIR / test_util.SHADER_JOB, donor_shader_job_paths=spirv_fuzz_shaders, seed=str( random.getrandbits( spirv_fuzz_util.GENERATE_SEED_BITS)), other_args=list(settings.extra_spirv_fuzz_generate_args) + list(settings.common_spirv_args), ) finally: gflogging.pop_stream_for_logging() except subprocess.CalledProcessError: util.mkdirs_p(fuzz_failures_dir) if len(list( fuzz_failures_dir.iterdir())) < settings.maximum_fuzz_failures: util.copy_dir(staging_dir, fuzz_failures_dir / staging_dir.name) return reference_name = reference_spirv_shader_job_orig_path.stem stable_shader = reference_name.startswith("stable_") common_spirv_args = list(settings.common_spirv_args) test_dirs = [ make_test( template_source_dir, staging_dir / f"{staging_name}_no_opt_test", spirv_opt_args=None, binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), make_test( template_source_dir, staging_dir / f"{staging_name}_opt_O_test", spirv_opt_args=["-O"], binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), make_test( template_source_dir, staging_dir / f"{staging_name}_opt_Os_test", spirv_opt_args=["-Os"], binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), make_test( template_source_dir, staging_dir / f"{staging_name}_opt_rand1_test", spirv_opt_args=spirv_opt_util.random_spirv_opt_args(), binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), make_test( template_source_dir, staging_dir / f"{staging_name}_opt_rand2_test", spirv_opt_args=spirv_opt_util.random_spirv_opt_args(), binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), make_test( template_source_dir, staging_dir / f"{staging_name}_opt_rand3_test", spirv_opt_args=spirv_opt_util.random_spirv_opt_args(), binary_manager=binary_manager, derived_from=reference_name, stable_shader=stable_shader, common_spirv_args=common_spirv_args, ), ] for test_dir in test_dirs: interrupt_util.interrupt_if_needed() if handle_test(test_dir, reports_dir, active_devices, binary_manager, settings): # If we generated a report, don't bother trying other optimization combinations. break
def run_spirv_reduce_or_shrink( # pylint: disable=too-many-locals; source_dir: Path, name_of_shader_job_to_reduce: str, extension_to_reduce: str, output_dir: Path, preserve_semantics: bool, binary_manager: binaries_util.BinaryManager, settings: Settings, ) -> Path: test = test_util.metadata_read_from_source_dir(source_dir) input_shader_job = source_dir / name_of_shader_job_to_reduce / test_util.SHADER_JOB original_spirv_file = input_shader_job.with_suffix( extension_to_reduce + shader_job_util.SUFFIX_SPIRV_ORIG) transformed_spirv_file = input_shader_job.with_suffix( extension_to_reduce + shader_job_util.SUFFIX_SPIRV) transformations_file = input_shader_job.with_suffix( extension_to_reduce + shader_job_util.SUFFIX_TRANSFORMATIONS) util.mkdirs_p(output_dir) final_shader = output_dir / "final.spv" # E.g. transformation_suffix_to_reduce == ".frag.transformations" # E.g. ".frag.??" -> ".frag.spv" shader_suffix_to_override = extension_to_reduce + shader_job_util.SUFFIX_SPIRV if preserve_semantics: cmd = [ str(binary_manager.get_binary_path_by_name("spirv-fuzz").path), str(original_spirv_file), "-o", str(final_shader), f"--shrink={str(transformations_file)}", f"--shrinker-temp-file-prefix={str(output_dir / 'temp_')}", ] cmd += list(settings.extra_spirv_fuzz_shrink_args) cmd += list(test.common_spirv_args) cmd += [ # This ensures the arguments that follow are all positional arguments. "--", "gfauto_interestingness_test", str(source_dir), # --override_shader requires three parameters to follow; the third will be added by spirv-fuzz (the shader.spv file). "--override_shader", name_of_shader_job_to_reduce, shader_suffix_to_override, ] else: cmd = [ str(binary_manager.get_binary_path_by_name("spirv-reduce").path), str(transformed_spirv_file), "-o", str(final_shader), f"--temp-file-prefix={str(output_dir / 'temp_')}", ] cmd += list(settings.extra_spirv_reduce_args) cmd += list(test.common_spirv_args) cmd += [ # This ensures the arguments that follow are all positional arguments. "--", "gfauto_interestingness_test", str(source_dir), # --override_shader requires three parameters to follow; the third will be added by spirv-reduce (the shader.spv file). "--override_shader", name_of_shader_job_to_reduce, shader_suffix_to_override, ] # Log the reduction. with util.file_open_text(output_dir / "command.log", "w") as f: gflogging.push_stream_for_logging(f) try: # The reducer can fail, but it will typically output an exception file, so we can ignore the exit code. subprocess_util.run(cmd, verbose=True, check_exit_code=False) finally: gflogging.pop_stream_for_logging() return final_shader