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 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 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 compile_shader_job( # pylint: disable=too-many-locals; name: str, input_json: Path, work_dir: Path, binary_paths: binaries_util.BinaryGetter, spirv_opt_args: Optional[List[str]] = None, shader_overrides: Optional[ShaderSuffixToShaderOverride] = None, preprocessor_cache: Optional[util.CommandCache] = None, skip_validation: bool = False, common_spirv_args: Optional[List[str]] = None, ) -> SpirvCombinedShaderJob: result = input_json glsl_source_shader_job: Optional[Path] = None glsl_suffixes = shader_job_util.get_related_suffixes_that_exist( result, language_suffix=(shader_job_util.SUFFIX_GLSL,) ) spirv_suffixes = shader_job_util.get_related_suffixes_that_exist( result, language_suffix=[shader_job_util.SUFFIX_SPIRV] ) # If GLSL: if glsl_suffixes: glsl_source_shader_job = result result = shader_job_util.copy(result, work_dir / "0_glsl" / result.name) if shader_overrides: raise AssertionError("Shader overrides are not supported for GLSL") result = glslang_glsl_shader_job_to_spirv( result, work_dir / "1_spirv" / result.name, binary_paths, preprocessor_cache=preprocessor_cache, ) # If SPIR-V: elif spirv_suffixes: result = shader_job_util.copy( result, work_dir / "1_spirv" / result.name, # Copy all spirv-fuzz related files too: language_suffix=shader_job_util.SUFFIXES_SPIRV_FUZZ, ) if shader_overrides: for suffix in spirv_suffixes: shader_override = shader_overrides.get(suffix) if shader_override: check( name == shader_override.name, AssertionError( f"shader job name {name} does not match shader override job name {shader_override.name}" ), ) check( shader_override.suffix == suffix, AssertionError( f"shader suffix {suffix} does not match shader override suffix {shader_override.suffix}" ), ) # These will be used as prefixes via .with_suffix(). # E.g. path/to/temp.spv source_prefix = shader_override.path # E.g. path/to/shader.json -> path/to/shader.frag.spv dest_prefix = result.with_suffix(suffix) util.copy_file_if_exists(source_prefix, dest_prefix) util.copy_file_if_exists( source_prefix.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS ), dest_prefix.with_suffix(shader_job_util.SUFFIX_TRANSFORMATIONS), ) util.copy_file_if_exists( source_prefix.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS_JSON ), dest_prefix.with_suffix( shader_job_util.SUFFIX_TRANSFORMATIONS_JSON ), ) else: # result has not changed, which means nothing was executed above. raise AssertionError(f"Unrecognized shader job type: {str(input_json)}") result_spirv = result result = spirv_dis_shader_job( result, work_dir / "1_spirv_asm" / result.name, binary_paths ) if not skip_validation: validate_spirv_shader_job( result_spirv, binary_paths, extra_args=common_spirv_args, ) if spirv_opt_args: result = result_spirv result = spirv_opt_shader_job( result, spirv_opt_args, work_dir / "2_spirv_opt" / result.name, binary_paths, preprocessor_cache=preprocessor_cache, skip_validation=skip_validation, ) result_spirv = result result = spirv_dis_shader_job( result, work_dir / "2_spirv_opt_asm" / result.name, binary_paths ) if not skip_validation: validate_spirv_shader_job( result_spirv, binary_paths, extra_args=common_spirv_args, ) return SpirvCombinedShaderJob( name=name, spirv_asm_shader_job=result, spirv_shader_job=result_spirv, glsl_source_shader_job=glsl_source_shader_job, )