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 run_reduction_on_report( test_dir: Path, reports_dir: Path, binary_manager: binaries_util.BinaryManager) -> None: test = test_util.metadata_read(test_dir) try: reduced_test = test_dir reduced_test = run_reduction( test_dir_reduction_output=test_dir, test_dir_to_reduce=reduced_test, preserve_semantics=True, binary_manager=binary_manager, reduction_name="1", ) if test.crash_signature != signature_util.BAD_IMAGE_SIGNATURE: reduced_test = run_reduction( test_dir_reduction_output=test_dir, test_dir_to_reduce=reduced_test, preserve_semantics=False, binary_manager=binary_manager, reduction_name="2", ) 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, 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 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