Esempio n. 1
0
def run_reduction_on_report(  # pylint: disable=too-many-locals;
    test_dir: Path,
    reports_dir: Path,
    binary_manager: binaries_util.BinaryManager,
    settings: Settings,
) -> None:
    test = test_util.metadata_read(test_dir)

    check(
        bool(test.device and test.device.name),
        AssertionError(
            f"Cannot reduce {str(test_dir)}; "
            f"device must be specified in {str(test_util.get_metadata_path(test_dir))}"
        ),
    )

    source_dir = test_util.get_source_dir(test_dir)

    try:
        run_reduction(
            source_dir_to_reduce=source_dir,
            reduction_output_dir=test_util.get_reductions_dir(
                test_dir, test.device.name),
            binary_manager=binary_manager,
            settings=settings,
        )
    except fuzz_glsl_test.ReductionFailedError as ex:
        # Create a symlink to the failed reduction so it is easy to investigate failed reductions.
        link_to_failed_reduction_path = (
            reports_dir / "failed_reductions" /
            f"{test_dir.name}_{ex.reduction_work_dir.name}")
        util.make_directory_symlink(
            new_symlink_file_path=link_to_failed_reduction_path,
            existing_dir=ex.reduction_work_dir,
        )
def run_reduction_on_report(
    test_dir: Path,
    reports_dir: Path,
    binary_manager: binaries_util.BinaryManager,
    settings: Settings,
) -> None:
    test = test_util.metadata_read(test_dir)

    if not test.device or not test.device.name:
        raise AssertionError(
            f"Cannot reduce {str(test_dir)}; device must be specified")

    try:
        run_reduction(
            source_dir_to_reduce=test_util.get_source_dir(test_dir),
            reduction_output_dir=test_util.get_reductions_dir(
                test_dir, test.device.name),
            binary_manager=binary_manager,
            settings=settings,
        )
    except ReductionFailedError as ex:
        # Create a symlink to the failed reduction so it is easy to investigate failed reductions.
        link_to_failed_reduction_path = (
            reports_dir / "failed_reductions" /
            f"{test_dir.name}_{ex.reduction_work_dir.name}")
        util.make_directory_symlink(
            new_symlink_file_path=link_to_failed_reduction_path,
            existing_dir=ex.reduction_work_dir,
        )
Esempio n. 3
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,
            )
Esempio n. 4
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
Esempio n. 5
0
def run(
    test_dir: Path,
    binary_manager: binaries_util.BinaryManager,
    device: Optional[Device] = None,
) -> str:

    test: Test = test_util.metadata_read(test_dir)
    if not device:
        device = test.device

    result_output_dir = run_shader_job(
        source_dir=test_util.get_source_dir(test_dir),
        output_dir=test_util.get_results_directory(test_dir, device.name),
        binary_manager=binary_manager,
        device=device,
    )

    return result_util.get_status(result_output_dir)
Esempio n. 6
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
Esempio n. 8
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))

    test.binaries.extend([binary_manager.get_binary_by_name(name="spirv-dis")])
    test.binaries.extend([binary_manager.get_binary_by_name(name="spirv-val")])
    if spirv_opt_args:
        test.binaries.extend(
            [binary_manager.get_binary_by_name(name="spirv-opt")])

    # Write the test metadata.
    test_util.metadata_write(test, subtest_dir)

    return subtest_dir
def run_reduction_part(
    reduction_part_output_dir: Path,
    source_dir_to_reduce: Path,
    preserve_semantics: bool,
    binary_manager: binaries_util.BinaryManager,
    settings: Settings,
) -> Path:
    test = test_util.metadata_read_from_source_dir(source_dir_to_reduce)

    if not test.device or not test.device.name:
        raise AssertionError(
            f"Cannot reduce {str(source_dir_to_reduce)}; "
            f"device must be specified in {str(test_util.get_metadata_path_from_source_dir(source_dir_to_reduce))}"
        )

    if not test.crash_signature:
        raise AssertionError(
            f"Cannot reduce {str(source_dir_to_reduce)} because there is no crash string specified."
        )

    shader_jobs = tool.get_shader_jobs(source_dir_to_reduce)

    # TODO: if needed, this could become a parameter to this function.
    name_of_shader_to_reduce = shader_jobs[0].name

    if len(shader_jobs) > 1:
        check(
            len(shader_jobs) == 2
            and shader_jobs[1].name == test_util.VARIANT_DIR,
            AssertionError(
                "Can only reduce tests with shader jobs reference and variant, or just variant."
            ),
        )
        name_of_shader_to_reduce = shader_jobs[1].name

    reduction_work_variant_dir = run_glsl_reduce(
        source_dir=source_dir_to_reduce,
        name_of_shader_to_reduce=name_of_shader_to_reduce,
        output_dir=test_util.get_reduction_work_directory(
            reduction_part_output_dir, name_of_shader_to_reduce),
        binary_manager=binary_manager,
        preserve_semantics=preserve_semantics,
        extra_args=list(settings.extra_graphics_fuzz_reduce_args)
        if settings.extra_graphics_fuzz_reduce_args else None,
    )

    final_reduced_shader_job_path = get_final_reduced_shader_job_path(
        reduction_work_variant_dir)

    check(
        final_reduced_shader_job_path.exists(),
        ReductionFailedError("Reduction failed.", reduction_work_variant_dir),
    )

    # Finally, create the output source_dir.

    util.copy_dir(source_dir_to_reduce,
                  test_util.get_source_dir(reduction_part_output_dir))

    shader_job_util.copy(
        final_reduced_shader_job_path,
        test_util.get_shader_job_path(reduction_part_output_dir,
                                      name_of_shader_to_reduce),
    )

    if not settings.keep_reduction_work:
        shutil.rmtree(reduction_work_variant_dir)

    return test_util.get_source_dir(reduction_part_output_dir)
Esempio n. 10
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
Esempio n. 11
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
Esempio n. 12
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)
Esempio n. 13
0
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,
    )
Esempio n. 14
0
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,
        )
Esempio n. 15
0
def run_reduction_on_report(  # pylint: disable=too-many-locals;
        test_dir: Path, reports_dir: Path,
        binary_manager: binaries_util.BinaryManager) -> None:
    test = test_util.metadata_read(test_dir)

    check(
        bool(test.device and test.device.name),
        AssertionError(
            f"Cannot reduce {str(test_dir)}; "
            f"device must be specified in {str(test_util.get_metadata_path(test_dir))}"
        ),
    )

    check(
        bool(test.crash_signature),
        AssertionError(
            f"Cannot reduce {str(test_dir)} because there is no crash string specified."
        ),
    )

    source_dir = test_util.get_source_dir(test_dir)

    shader_jobs = tool.get_shader_jobs(source_dir)

    # TODO: if needed, this could become a parameter to this function.
    shader_job_to_reduce = shader_jobs[0]

    if len(shader_jobs) > 1:
        check(
            len(shader_jobs) == 2
            and shader_jobs[1].name == test_util.VARIANT_DIR,
            AssertionError(
                "Can only reduce tests with shader jobs reference and variant, or just variant."
            ),
        )
        shader_job_to_reduce = shader_jobs[1]

    shader_transformation_suffixes = shader_job_util.get_related_suffixes_that_exist(
        shader_job_to_reduce.shader_job,
        language_suffix=(shader_job_util.SUFFIX_TRANSFORMATIONS, ),
    )

    shader_spv_suffixes = shader_job_util.get_related_suffixes_that_exist(
        shader_job_to_reduce.shader_job,
        language_suffix=(shader_job_util.SUFFIX_SPIRV, ))

    try:
        reduced_test = test_dir

        for index, suffix in enumerate(shader_transformation_suffixes):
            # E.g. .frag.transformations -> .frag
            extension_to_reduce = str(Path(suffix).with_suffix(""))
            reduced_test = run_reduction(
                test_dir_reduction_output=test_dir,
                test_dir_to_reduce=reduced_test,
                shader_job_name_to_reduce=shader_job_to_reduce.name,
                extension_to_reduce=extension_to_reduce,
                preserve_semantics=True,
                binary_manager=binary_manager,
                reduction_name=f"0_{index}_{suffix.split('.')[1]}",
            )

        if test.crash_signature != signature_util.BAD_IMAGE_SIGNATURE:
            for index, suffix in enumerate(shader_spv_suffixes):
                # E.g. .frag.spv -> .frag
                extension_to_reduce = str(Path(suffix).with_suffix(""))
                reduced_test = run_reduction(
                    test_dir_reduction_output=test_dir,
                    test_dir_to_reduce=reduced_test,
                    shader_job_name_to_reduce=shader_job_to_reduce.name,
                    extension_to_reduce=extension_to_reduce,
                    preserve_semantics=False,
                    binary_manager=binary_manager,
                    reduction_name=f"1_{index}_{suffix.split('.')[1]}",
                )

        device_name = test.device.name

        # Create a symlink to the "best" reduction.
        best_reduced_test_link = test_util.get_reduced_test_dir(
            test_dir, device_name, fuzz.BEST_REDUCTION_NAME)
        util.make_directory_symlink(
            new_symlink_file_path=best_reduced_test_link,
            existing_dir=reduced_test)
    except ReductionFailedError as ex:
        # Create a symlink to the failed reduction so it is easy to investigate failed reductions.
        link_to_failed_reduction_path = (
            reports_dir / "failed_reductions" /
            f"{test_dir.name}_{ex.reduction_name}")
        util.make_directory_symlink(
            new_symlink_file_path=link_to_failed_reduction_path,
            existing_dir=ex.reduction_work_dir,
        )
Esempio n. 16
0
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