def run_reduction_on_report( # pylint: disable=too-many-locals; test_dir: Path, reports_dir: Path, binary_manager: binaries_util.BinaryManager, settings: Settings, ) -> None: test = test_util.metadata_read(test_dir) check( bool(test.device and test.device.name), AssertionError( f"Cannot reduce {str(test_dir)}; " f"device must be specified in {str(test_util.get_metadata_path(test_dir))}" ), ) source_dir = test_util.get_source_dir(test_dir) try: run_reduction( source_dir_to_reduce=source_dir, reduction_output_dir=test_util.get_reductions_dir( test_dir, test.device.name), binary_manager=binary_manager, settings=settings, ) except fuzz_glsl_test.ReductionFailedError as ex: # Create a symlink to the failed reduction so it is easy to investigate failed reductions. link_to_failed_reduction_path = ( reports_dir / "failed_reductions" / f"{test_dir.name}_{ex.reduction_work_dir.name}") util.make_directory_symlink( new_symlink_file_path=link_to_failed_reduction_path, existing_dir=ex.reduction_work_dir, )
def run_reduction_on_report( test_dir: Path, reports_dir: Path, binary_manager: binaries_util.BinaryManager, settings: Settings, ) -> None: test = test_util.metadata_read(test_dir) if not test.device or not test.device.name: raise AssertionError( f"Cannot reduce {str(test_dir)}; device must be specified") try: run_reduction( source_dir_to_reduce=test_util.get_source_dir(test_dir), reduction_output_dir=test_util.get_reductions_dir( test_dir, test.device.name), binary_manager=binary_manager, settings=settings, ) except ReductionFailedError as ex: # Create a symlink to the failed reduction so it is easy to investigate failed reductions. link_to_failed_reduction_path = ( reports_dir / "failed_reductions" / f"{test_dir.name}_{ex.reduction_work_dir.name}") util.make_directory_symlink( new_symlink_file_path=link_to_failed_reduction_path, existing_dir=ex.reduction_work_dir, )
def create_summary_and_reproduce( test_dir: Path, binary_manager: binaries_util.BinaryManager) -> None: test_metadata = test_util.metadata_read(test_dir) summary_dir = test_dir / "summary" unreduced = util.copy_dir(test_util.get_source_dir(test_dir), summary_dir / "unreduced") reduced_test_dir = test_util.get_reduced_test_dir( test_dir, test_metadata.device.name, fuzz.BEST_REDUCTION_NAME) reduced_source_dir = test_util.get_source_dir(reduced_test_dir) reduced: Optional[Path] = None if reduced_source_dir.exists(): reduced = util.copy_dir(reduced_source_dir, summary_dir / "reduced") run_shader_job( source_dir=unreduced, output_dir=(summary_dir / "unreduced_result"), binary_manager=binary_manager, ) variant_reduced_glsl_result: Optional[Path] = None if reduced: variant_reduced_glsl_result = run_shader_job( source_dir=reduced, output_dir=(summary_dir / "reduced_result"), binary_manager=binary_manager, ) # Some post-processing for common error types. if variant_reduced_glsl_result: status = result_util.get_status(variant_reduced_glsl_result) if status == fuzz.STATUS_TOOL_CRASH: tool_crash_summary_bug_report_dir( reduced_source_dir, variant_reduced_glsl_result, summary_dir, binary_manager, )
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)) fuzz_glsl_test.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 run( test_dir: Path, binary_manager: binaries_util.BinaryManager, device: Optional[Device] = None, ) -> str: test: Test = test_util.metadata_read(test_dir) if not device: device = test.device result_output_dir = run_shader_job( source_dir=test_util.get_source_dir(test_dir), output_dir=test_util.get_results_directory(test_dir, device.name), binary_manager=binary_manager, device=device, ) return result_util.get_status(result_output_dir)
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 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_reduction_part( reduction_part_output_dir: Path, source_dir_to_reduce: Path, preserve_semantics: bool, binary_manager: binaries_util.BinaryManager, settings: Settings, ) -> Path: test = test_util.metadata_read_from_source_dir(source_dir_to_reduce) if not test.device or not test.device.name: raise AssertionError( f"Cannot reduce {str(source_dir_to_reduce)}; " f"device must be specified in {str(test_util.get_metadata_path_from_source_dir(source_dir_to_reduce))}" ) if not test.crash_signature: raise AssertionError( f"Cannot reduce {str(source_dir_to_reduce)} because there is no crash string specified." ) shader_jobs = tool.get_shader_jobs(source_dir_to_reduce) # TODO: if needed, this could become a parameter to this function. name_of_shader_to_reduce = shader_jobs[0].name if len(shader_jobs) > 1: check( len(shader_jobs) == 2 and shader_jobs[1].name == test_util.VARIANT_DIR, AssertionError( "Can only reduce tests with shader jobs reference and variant, or just variant." ), ) name_of_shader_to_reduce = shader_jobs[1].name reduction_work_variant_dir = run_glsl_reduce( source_dir=source_dir_to_reduce, name_of_shader_to_reduce=name_of_shader_to_reduce, output_dir=test_util.get_reduction_work_directory( reduction_part_output_dir, name_of_shader_to_reduce), binary_manager=binary_manager, preserve_semantics=preserve_semantics, extra_args=list(settings.extra_graphics_fuzz_reduce_args) if settings.extra_graphics_fuzz_reduce_args else None, ) final_reduced_shader_job_path = get_final_reduced_shader_job_path( reduction_work_variant_dir) check( final_reduced_shader_job_path.exists(), ReductionFailedError("Reduction failed.", reduction_work_variant_dir), ) # Finally, create the output source_dir. util.copy_dir(source_dir_to_reduce, test_util.get_source_dir(reduction_part_output_dir)) shader_job_util.copy( final_reduced_shader_job_path, test_util.get_shader_job_path(reduction_part_output_dir, name_of_shader_to_reduce), ) if not settings.keep_reduction_work: shutil.rmtree(reduction_work_variant_dir) return test_util.get_source_dir(reduction_part_output_dir)
def run_reduction( test_dir_reduction_output: Path, test_dir_to_reduce: Path, preserve_semantics: bool, binary_manager: binaries_util.BinaryManager, reduction_name: str = "reduction1", ) -> Path: test = test_util.metadata_read(test_dir_to_reduce) if not test.device or not test.device.name: raise AssertionError( f"Cannot reduce {str(test_dir_to_reduce)}; " f"device must be specified in {str(test_util.get_metadata_path(test_dir_to_reduce))}" ) if not test.crash_signature: raise AssertionError( f"Cannot reduce {str(test_dir_to_reduce)} because there is no crash string specified." ) # E.g. reports/crashes/no_signature/d50c96e8_opt_rand2_test_phone_ABC/results/phone_ABC/reductions/1 # Will contain work/ and source/ reduced_test_dir = test_util.get_reduced_test_dir( test_dir_reduction_output, test.device.name, reduction_name) source_dir = test_util.get_source_dir(test_dir_to_reduce) shader_jobs = tool.get_shader_jobs(source_dir) # TODO: if needed, this could become a parameter to this function. name_of_shader_to_reduce = shader_jobs[0].name if len(shader_jobs) > 1: check( len(shader_jobs) == 2 and shader_jobs[1].name == test_util.VARIANT_DIR, AssertionError( "Can only reduce tests with shader jobs reference and variant, or just variant." ), ) name_of_shader_to_reduce = shader_jobs[1].name reduction_work_variant_dir = run_glsl_reduce( source_dir=source_dir, name_of_shader_to_reduce=name_of_shader_to_reduce, output_dir=test_util.get_reduction_work_directory( reduced_test_dir, name_of_shader_to_reduce), binary_manager=binary_manager, preserve_semantics=preserve_semantics, ) final_reduced_shader_job_path = get_final_reduced_shader_job_path( reduction_work_variant_dir) check( final_reduced_shader_job_path.exists(), ReductionFailedError("Reduction failed.", reduction_name, reduction_work_variant_dir), ) # Finally, create the source_dir so the returned directory can be used as a test_dir. util.copy_dir(source_dir, test_util.get_source_dir(reduced_test_dir)) shader_job_util.copy( final_reduced_shader_job_path, test_util.get_shader_job_path(reduced_test_dir, name_of_shader_to_reduce), ) return reduced_test_dir
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
def run_reduction_part( reduction_part_output_dir: Path, source_dir_to_reduce: Path, shader_job_name_to_reduce: str, extension_to_reduce: str, preserve_semantics: bool, binary_manager: binaries_util.BinaryManager, settings: Settings, ) -> Path: test = test_util.metadata_read_from_source_dir(source_dir_to_reduce) check( bool(test.device and test.device.name), AssertionError( f"Cannot reduce {str(source_dir_to_reduce)}; device must be specified" ), ) check( bool(test.crash_signature), AssertionError( f"Cannot reduce {str(source_dir_to_reduce)} because there is no crash string specified." ), ) output_dir = test_util.get_reduction_work_directory( reduction_part_output_dir, shader_job_name_to_reduce) final_shader_path = run_spirv_reduce_or_shrink( source_dir=source_dir_to_reduce, name_of_shader_job_to_reduce=shader_job_name_to_reduce, extension_to_reduce=extension_to_reduce, output_dir=output_dir, preserve_semantics=preserve_semantics, binary_manager=binary_manager, settings=settings, ) check( final_shader_path.exists(), fuzz_glsl_test.ReductionFailedError("Reduction failed.", output_dir), ) # Finally, create the source_dir so the returned directory can be used as a test_dir. # Copy the original source directory. util.copy_dir(source_dir_to_reduce, test_util.get_source_dir(reduction_part_output_dir)) # And then replace the shader. # Destination file. E.g. reductions/source/variant/shader.frag.spv output_shader_prefix = ( test_util.get_source_dir(reduction_part_output_dir) / shader_job_name_to_reduce / test_util.SHADER_JOB).with_suffix(extension_to_reduce + shader_job_util.SUFFIX_SPIRV) util.copy_file( final_shader_path.with_suffix(shader_job_util.SUFFIX_SPIRV), output_shader_prefix.with_suffix(shader_job_util.SUFFIX_SPIRV), ) if preserve_semantics: util.copy_file( final_shader_path.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS), output_shader_prefix.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS), ) util.copy_file( final_shader_path.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS_JSON), output_shader_prefix.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS_JSON), ) return test_util.get_source_dir(reduction_part_output_dir)
def main() -> None: # pylint: disable=too-many-locals; parser = argparse.ArgumentParser( description="Generates an AmberScript test from a shader job.") parser.add_argument( "shader_job", help="The input .json shader job file.", ) parser.add_argument( "--output", help="Output directory.", default="output", ) parser.add_argument( "--spirv_opt_args", help= "Arguments for spirv-opt as a space-separated string, or an empty string to skip running spirv-opt.", default="", ) parser.add_argument( "--settings", help= "Path to a settings JSON file for this instance. The file will be generated if needed. ", default="settings.json", ) parsed_args = parser.parse_args(sys.argv[1:]) shader_job: Path = Path(parsed_args.shader_job) out_dir: Path = Path(parsed_args.output) spirv_opt_args_str: str = parsed_args.spirv_opt_args settings_path: Path = Path(parsed_args.settings) spirv_opt_args: List[str] = [] if spirv_opt_args_str: spirv_opt_args = spirv_opt_args_str.split(" ") settings = settings_util.read_or_create(settings_path) binary_manager = binaries_util.get_default_binary_manager(settings) staging_dir = out_dir / "staging" template_source_dir = staging_dir / "source_template" test_dir = staging_dir / "test" run_output_dir: Path = out_dir / "run" # Remove stale directories. if staging_dir.is_dir(): shutil.rmtree(staging_dir) if run_output_dir.is_dir(): shutil.rmtree(run_output_dir) # Create source template and call |make_test|. if shader_job_util.get_related_suffixes_that_exist( shader_job, language_suffix=[shader_job_util.SUFFIX_SPIRV]): # This is a SPIR-V shader job. shader_job_util.copy( shader_job, template_source_dir / test_util.VARIANT_DIR / test_util.SHADER_JOB, language_suffix=shader_job_util.SUFFIXES_SPIRV_FUZZ_INPUT, ) fuzz_spirv_amber_test.make_test( template_source_dir, test_dir, spirv_opt_args=spirv_opt_args, binary_manager=binary_manager, derived_from=shader_job.stem, stable_shader=False, common_spirv_args=list(settings.common_spirv_args), ) elif shader_job_util.get_related_suffixes_that_exist( shader_job, language_suffix=[shader_job_util.SUFFIX_GLSL]): # This is a GLSL shader job. # 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), ) 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, shader_job, template_source_dir / test_util.VARIANT_DIR / test_util.SHADER_JOB, legacy_graphics_fuzz_vulkan_arg=settings. legacy_graphics_fuzz_vulkan_arg, ) finally: gflogging.pop_stream_for_logging() fuzz_glsl_amber_test.make_test( template_source_dir, test_dir, spirv_opt_args=spirv_opt_args, binary_manager=binary_manager, derived_from=shader_job.stem, stable_shader=False, common_spirv_args=list(settings.common_spirv_args), ) else: raise AssertionError( "Unexpected shader job type; expected GLSL or SPIR-V shaders.") preprocessor_cache = util.CommandCache() fuzz_test_util.run_shader_job( source_dir=test_util.get_source_dir(test_dir), output_dir=run_output_dir, binary_manager=binary_manager, device=Device(host=DeviceHost()), preprocessor_cache=preprocessor_cache, stop_after_amber=True, )
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"): fuzz_spirv_test.create_spirv_fuzz_variant_2( reduced_1, binary_manager, 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 run_reduction_on_report( # pylint: disable=too-many-locals; test_dir: Path, reports_dir: Path, binary_manager: binaries_util.BinaryManager) -> None: test = test_util.metadata_read(test_dir) check( bool(test.device and test.device.name), AssertionError( f"Cannot reduce {str(test_dir)}; " f"device must be specified in {str(test_util.get_metadata_path(test_dir))}" ), ) check( bool(test.crash_signature), AssertionError( f"Cannot reduce {str(test_dir)} because there is no crash string specified." ), ) source_dir = test_util.get_source_dir(test_dir) shader_jobs = tool.get_shader_jobs(source_dir) # TODO: if needed, this could become a parameter to this function. shader_job_to_reduce = shader_jobs[0] if len(shader_jobs) > 1: check( len(shader_jobs) == 2 and shader_jobs[1].name == test_util.VARIANT_DIR, AssertionError( "Can only reduce tests with shader jobs reference and variant, or just variant." ), ) shader_job_to_reduce = shader_jobs[1] shader_transformation_suffixes = shader_job_util.get_related_suffixes_that_exist( shader_job_to_reduce.shader_job, language_suffix=(shader_job_util.SUFFIX_TRANSFORMATIONS, ), ) shader_spv_suffixes = shader_job_util.get_related_suffixes_that_exist( shader_job_to_reduce.shader_job, language_suffix=(shader_job_util.SUFFIX_SPIRV, )) try: reduced_test = test_dir for index, suffix in enumerate(shader_transformation_suffixes): # E.g. .frag.transformations -> .frag extension_to_reduce = str(Path(suffix).with_suffix("")) reduced_test = run_reduction( test_dir_reduction_output=test_dir, test_dir_to_reduce=reduced_test, shader_job_name_to_reduce=shader_job_to_reduce.name, extension_to_reduce=extension_to_reduce, preserve_semantics=True, binary_manager=binary_manager, reduction_name=f"0_{index}_{suffix.split('.')[1]}", ) if test.crash_signature != signature_util.BAD_IMAGE_SIGNATURE: for index, suffix in enumerate(shader_spv_suffixes): # E.g. .frag.spv -> .frag extension_to_reduce = str(Path(suffix).with_suffix("")) reduced_test = run_reduction( test_dir_reduction_output=test_dir, test_dir_to_reduce=reduced_test, shader_job_name_to_reduce=shader_job_to_reduce.name, extension_to_reduce=extension_to_reduce, preserve_semantics=False, binary_manager=binary_manager, reduction_name=f"1_{index}_{suffix.split('.')[1]}", ) device_name = test.device.name # Create a symlink to the "best" reduction. best_reduced_test_link = test_util.get_reduced_test_dir( test_dir, device_name, fuzz.BEST_REDUCTION_NAME) util.make_directory_symlink( new_symlink_file_path=best_reduced_test_link, existing_dir=reduced_test) except ReductionFailedError as ex: # Create a symlink to the failed reduction so it is easy to investigate failed reductions. link_to_failed_reduction_path = ( reports_dir / "failed_reductions" / f"{test_dir.name}_{ex.reduction_name}") util.make_directory_symlink( new_symlink_file_path=link_to_failed_reduction_path, existing_dir=ex.reduction_work_dir, )
def run_reduction( test_dir_reduction_output: Path, test_dir_to_reduce: Path, shader_job_name_to_reduce: str, extension_to_reduce: str, preserve_semantics: bool, binary_manager: binaries_util.BinaryManager, reduction_name: str = "reduction1", ) -> Path: test = test_util.metadata_read(test_dir_to_reduce) check( bool(test.device and test.device.name), AssertionError( f"Cannot reduce {str(test_dir_to_reduce)}; " f"device must be specified in {str(test_util.get_metadata_path(test_dir_to_reduce))}" ), ) check( bool(test.crash_signature), AssertionError( f"Cannot reduce {str(test_dir_to_reduce)} because there is no crash string specified." ), ) # E.g. reports/crashes/no_signature/d50c96e8_opt_rand2_test_phone_ABC/results/phone_ABC/reductions/1 # Will contain work/ and source/ reduced_test_dir = test_util.get_reduced_test_dir( test_dir_reduction_output, test.device.name, reduction_name) source_dir = test_util.get_source_dir(test_dir_to_reduce) output_dir = test_util.get_reduction_work_directory( reduced_test_dir, shader_job_name_to_reduce) if preserve_semantics: final_shader_path = run_spirv_reduce_or_shrink( source_dir=source_dir, name_of_shader_job_to_reduce=shader_job_name_to_reduce, extension_to_reduce=extension_to_reduce, output_dir=output_dir, preserve_semantics=preserve_semantics, binary_manager=binary_manager, ) else: final_shader_path = run_spirv_reduce_or_shrink( source_dir=source_dir, name_of_shader_job_to_reduce=shader_job_name_to_reduce, extension_to_reduce=extension_to_reduce, output_dir=output_dir, preserve_semantics=preserve_semantics, binary_manager=binary_manager, ) check( final_shader_path.exists(), ReductionFailedError("Reduction failed.", reduction_name, output_dir), ) # Finally, create the source_dir so the returned directory can be used as a test_dir. # Copy the original source directory. util.copy_dir(source_dir, test_util.get_source_dir(reduced_test_dir)) # And then replace the shader. # Destination file. E.g. reductions/source/variant/shader.frag.spv output_shader_prefix = ( test_util.get_source_dir(reduced_test_dir) / shader_job_name_to_reduce / test_util.SHADER_JOB).with_suffix(extension_to_reduce + shader_job_util.SUFFIX_SPIRV) util.copy_file( final_shader_path.with_suffix(shader_job_util.SUFFIX_SPIRV), output_shader_prefix.with_suffix(shader_job_util.SUFFIX_SPIRV), ) if preserve_semantics: util.copy_file( final_shader_path.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS), output_shader_prefix.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS), ) util.copy_file( final_shader_path.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS_JSON), output_shader_prefix.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS_JSON), ) return reduced_test_dir