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 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 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)) 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 download_cts_graphicsfuzz_tests( # pylint: disable=too-many-locals; git_tool: Path, cookie: str, binaries: binaries_util.BinaryManager) -> None: work_dir = Path() / "temp" / ("cts_" + fuzz.get_random_name()) latest_change = gerrit_util.get_latest_deqp_change(cookie) latest_change_number = latest_change["_number"] latest_change_details = gerrit_util.get_gerrit_change_details( change_number=latest_change_number, cookie=cookie) current_revision = latest_change_details["current_revision"] cts_archive_path = gerrit_util.download_gerrit_revision( output_path=work_dir / "cts.tgz", change_number=latest_change_number, revision=current_revision, download_type=gerrit_util.DownloadType.Archive, cookie=cookie, ) cts_dir_name = "cts_temp" cts_out = util.extract_archive(cts_archive_path, work_dir / cts_dir_name) pending_graphicsfuzz_changes = gerrit_util.get_deqp_graphicsfuzz_pending_changes( cookie) for pending_change in pending_graphicsfuzz_changes: change_number = pending_change["_number"] change_details = gerrit_util.get_gerrit_change_details( change_number=change_number, cookie=cookie) current_revision = change_details["current_revision"] patch_zip = gerrit_util.download_gerrit_revision( output_path=work_dir / f"{change_number}.zip", change_number=change_number, revision=current_revision, download_type=gerrit_util.DownloadType.Patch, cookie=cookie, ) util.extract_archive(patch_zip, work_dir) # Create a dummy git repo in the work directory, otherwise "git apply" can fail silently. # --unsafe-paths is possibly supposed to address this, but it doesn't seem to work if we # are already in a git repo. subprocess_util.run([str(git_tool), "init", "."], verbose=True, working_dir=work_dir) cmd = [str(git_tool), "apply"] patch_names = [p.name for p in work_dir.glob("*.diff")] cmd += patch_names # Use unix-style path for git. cmd += [ "--verbose", "--unsafe-paths", f"--directory={cts_dir_name}", f"--include={cts_dir_name}/external/vulkancts/data/vulkan/amber/graphicsfuzz/*", ] subprocess_util.run(cmd, verbose=True, working_dir=work_dir) shader_dir = util.copy_dir( cts_out / "external" / "vulkancts" / "data" / "vulkan" / "amber" / "graphicsfuzz", Path() / "graphicsfuzz", ) for amber_file in shader_dir.glob("*.amber"): amber_converter.extract_shaders(amber_file, output_dir=amber_file.parent, binaries=binaries) zip_files = [ util.ZipEntry(f, Path(f.name)) for f in sorted(shader_dir.glob(f"{amber_file.stem}.*")) ] util.create_zip(amber_file.with_suffix(".zip"), zip_files)
def tool_crash_summary_bug_report_dir( # pylint: disable=too-many-locals; reduced_glsl_source_dir: Path, variant_reduced_glsl_result_dir: Path, output_dir: Path, binary_manager: binaries_util.BinaryManager, ) -> Optional[Path]: # Create a simple script and README. shader_job = reduced_glsl_source_dir / test_util.VARIANT_DIR / test_util.SHADER_JOB if not shader_job.is_file(): return None test_metadata: Test = test_util.metadata_read_from_path( reduced_glsl_source_dir / test_util.TEST_METADATA) shader_files = shader_job_util.get_related_files( shader_job, shader_job_util.EXT_ALL, (shader_job_util.SUFFIX_GLSL, shader_job_util.SUFFIX_SPIRV), ) check( len(shader_files) > 0, AssertionError(f"Need at least one shader for {shader_job}"), ) shader_extension = shader_files[0].suffix bug_report_dir = util.copy_dir(variant_reduced_glsl_result_dir, output_dir / "bug_report") shader_files = sorted(bug_report_dir.rglob("shader.*")) glsl_files = [ shader_file for shader_file in shader_files if shader_file.suffix == shader_extension ] asm_files = [ shader_file for shader_file in shader_files if shader_file.name.endswith(shader_extension + shader_job_util.SUFFIX_ASM_SPIRV) ] spv_files = [ shader_file for shader_file in shader_files if shader_file.name.endswith(shader_extension + shader_job_util.SUFFIX_SPIRV) ] readme = "\n\n" readme += ( "Issue found using [GraphicsFuzz](https://github.com/google/graphicsfuzz).\n\n" ) readme += "Tool versions:\n\n" # noinspection PyTypeChecker if test_metadata.HasField("glsl"): readme += f"* glslangValidator commit hash: {binary_manager.get_binary_by_name(binaries_util.GLSLANG_VALIDATOR_NAME).version}\n" if test_metadata.glsl.spirv_opt_args or test_metadata.spirv_fuzz.spirv_opt_args: readme += f"* spirv-opt commit hash: {binary_manager.get_binary_by_name(binaries_util.SPIRV_OPT_NAME).version}\n" readme += "\nTo reproduce:\n\n" readme += f"`glslangValidator -V shader{shader_extension} -o shader{shader_extension}.spv`\n\n" if (test_metadata.HasField("glsl") and spv_files and not test_metadata.glsl.spirv_opt_args): # GLSL was converted to SPIR-V, and spirv-opt was not run, so indicate that we should validate the SPIR-V. readme += f"`spirv-val shader{shader_extension}.spv`\n\n" if test_metadata.glsl.spirv_opt_args or test_metadata.spirv_fuzz.spirv_opt_args: readme += f"`spirv-opt shader{shader_extension}.spv -o temp.spv --validate-after-all {' '.join(test_metadata.glsl.spirv_opt_args)}`\n\n" files_to_list = glsl_files + spv_files + asm_files files_to_list.sort() files_to_show = glsl_files + asm_files files_to_show.sort() readme += "The following shader files are included in the attached archive, some of which are also shown inline below:\n\n" for file_to_list in files_to_list: short_path = file_to_list.relative_to(bug_report_dir).as_posix() readme += f"* {short_path}\n" for file_to_show in files_to_show: short_path = file_to_show.relative_to(bug_report_dir).as_posix() file_contents = util.file_read_text(file_to_show) readme += f"\n{short_path}:\n\n" readme += f"```\n{file_contents}\n```\n" util.file_write_text(output_dir / "README.md", readme) return bug_report_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 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 fuzz_and_reduce_bug( active_device: str, seed: int, check_result: Callable[[], None], settings: Optional[Settings] = None, ignored_signatures: Optional[List[str]] = None, ) -> None: """ Fuzz, find a bug, reduce it. Linux only. """ # Test only works on Linux. if util.get_platform() != "Linux": return here = util.norm_path(Path(__file__).absolute()).parent temp_dir: Path = here.parent / "temp" assert temp_dir.is_dir() os.chdir(temp_dir) # Create ROOT file in temp/ if needed. fuzz.try_get_root_file() work_dir = temp_dir / fuzz.get_random_name()[:8] util.mkdir_p_new(work_dir) os.chdir(work_dir) log(f"Changed to {str(work_dir)}") if settings is None: settings = Settings() settings.CopyFrom(settings_util.DEFAULT_SETTINGS) settings.device_list.CopyFrom( DeviceList( active_device_names=[active_device], devices=[ Device( name="amdllpc", shader_compiler=DeviceShaderCompiler( binary="amdllpc", args=[ "-gfxip=9.0.0", "-verify-ir", "-auto-layout-desc" ], ), binaries=[ Binary( name="amdllpc", tags=["Release"], version="c21d76dceaf26361f9b6b3838a955ec3301506b5", ), ], ), Device( name="swift_shader", swift_shader=DeviceSwiftShader(), binaries=[ Binary( name="swift_shader_icd", tags=["Release"], version="6d69aae0e1ab49190ea46cd1c999fd3d02e016b9", ), ], ignored_crash_signatures=ignored_signatures, ), ], )) spirv_tools_version = "983b5b4fccea17cab053de24d51403efb4829158" settings.latest_binary_versions.extend([ Binary( name="glslangValidator", tags=["Release"], version="1afa2b8cc57b92c6b769eb44a6854510b6921a0b", ), Binary(name="spirv-opt", tags=["Release"], version=spirv_tools_version), Binary(name="spirv-dis", tags=["Release"], version=spirv_tools_version), Binary(name="spirv-as", tags=["Release"], version=spirv_tools_version), Binary(name="spirv-val", tags=["Release"], version=spirv_tools_version), Binary(name="spirv-fuzz", tags=["Release"], version=spirv_tools_version), Binary(name="spirv-reduce", tags=["Release"], version=spirv_tools_version), Binary( name="graphicsfuzz-tool", tags=[], version="7b143bcb3ad38b64ddc17d132886636b229b6684", ), ]) # Add default binaries; the ones above have priority. settings.latest_binary_versions.extend(binaries_util.DEFAULT_BINARIES) settings.extra_graphics_fuzz_generate_args.append("--small") settings.extra_graphics_fuzz_generate_args.append("--single-pass") settings.extra_graphics_fuzz_reduce_args.append("--max-steps") settings.extra_graphics_fuzz_reduce_args.append("2") settings_util.write(settings, settings_util.DEFAULT_SETTINGS_FILE_PATH) # Add shaders. binary_manager = binaries_util.get_default_binary_manager(settings) graphicsfuzz_tool = binary_manager.get_binary_path_by_name( "graphicsfuzz-tool") sample_shaders_path: Path = graphicsfuzz_tool.path.parent.parent.parent / "shaders" / "samples" / "310es" util.copy_dir(sample_shaders_path, Path() / fuzz.REFERENCES_DIR) util.copy_dir(sample_shaders_path, Path() / fuzz.DONORS_DIR) fuzz.main_helper( settings_path=settings_util.DEFAULT_SETTINGS_FILE_PATH, iteration_seed_override=seed, override_sigint=False, use_amber_vulkan_loader=True, ) check_result() os.chdir(here) shutil.rmtree(work_dir)
def download_cts_graphicsfuzz_tests( # pylint: disable=too-many-locals; git_tool: Path, cookie: str, output_tests_dir: Path, ) -> Path: work_dir = Path() / "temp" / ("cts_" + fuzz.get_random_name()) latest_change = gerrit_util.get_latest_deqp_change(cookie) latest_change_number = latest_change["_number"] latest_change_details = gerrit_util.get_gerrit_change_details( change_number=latest_change_number, cookie=cookie) current_revision = latest_change_details["current_revision"] cts_archive_path = gerrit_util.download_gerrit_revision( output_path=work_dir / "cts.tgz", change_number=latest_change_number, revision=current_revision, download_type=gerrit_util.DownloadType.Archive, cookie=cookie, ) cts_dir_name = "cts_temp" cts_out = util.extract_archive(cts_archive_path, work_dir / cts_dir_name) pending_graphicsfuzz_changes = gerrit_util.get_deqp_graphicsfuzz_pending_changes( cookie) for pending_change in pending_graphicsfuzz_changes: change_number = pending_change["_number"] change_details = gerrit_util.get_gerrit_change_details( change_number=change_number, cookie=cookie) current_revision = change_details["current_revision"] patch_zip = gerrit_util.download_gerrit_revision( output_path=work_dir / f"{change_number}.zip", change_number=change_number, revision=current_revision, download_type=gerrit_util.DownloadType.Patch, cookie=cookie, ) util.extract_archive(patch_zip, work_dir) # Create a dummy git repo in the work directory, otherwise "git apply" can fail silently. # --unsafe-paths is possibly supposed to address this, but it doesn't seem to work if we # are already in a git repo. subprocess_util.run([str(git_tool), "init", "."], verbose=True, working_dir=work_dir) cmd = [str(git_tool), "apply"] patch_names = [p.name for p in work_dir.glob("*.diff")] cmd += patch_names # Use unix-style path for git. cmd += [ "--verbose", "--unsafe-paths", f"--directory={cts_dir_name}", f"--exclude={cts_dir_name}/external/vulkancts/data/vulkan/amber/graphicsfuzz/index.txt", f"--include={cts_dir_name}/external/vulkancts/data/vulkan/amber/graphicsfuzz/*", ] subprocess_util.run(cmd, verbose=True, working_dir=work_dir) util.copy_dir( cts_out / "external" / "vulkancts" / "data" / "vulkan" / "amber" / "graphicsfuzz", output_tests_dir, ) # Sometimes dEQP contributors add non-GraphicsFuzz AmberScript files to the graphicsfuzz directory. # We remove these. bad_test_names = ["texel_offset.amber"] for bad_test_name in bad_test_names: bad_test = output_tests_dir / bad_test_name if bad_test.is_file(): bad_test.unlink() return output_tests_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 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 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 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