Exemple #1
0
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
Exemple #2
0
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
Exemple #4
0
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)
Exemple #7
0
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
Exemple #8
0
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
Exemple #9
0
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
Exemple #10
0
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,
        )
Exemple #12
0
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)
Exemple #15
0
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