From 9722976911c2f4cb8d911d217683f3468c63b7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Tue, 11 Apr 2023 18:27:45 +0200 Subject: [PATCH 01/26] implement FeatureBlameReportExperiment --- .../data/reports/feature_blame_report.py | 228 ++++++++++++++++++ .../vara/feature_blame_experiment.py | 73 ++++++ .../vara/feature_blame_report_experiment.py | 138 +++++++++++ 3 files changed, 439 insertions(+) create mode 100644 varats/varats/data/reports/feature_blame_report.py create mode 100644 varats/varats/experiments/vara/feature_blame_experiment.py create mode 100644 varats/varats/experiments/vara/feature_blame_report_experiment.py diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py new file mode 100644 index 000000000..0d886221d --- /dev/null +++ b/varats/varats/data/reports/feature_blame_report.py @@ -0,0 +1,228 @@ +"""Module for FeatureBlameReport.""" + +import re +import typing as tp +from pathlib import Path + +import yaml + +from varats.base.version_header import VersionHeader +from varats.report.report import BaseReport + + +class FeatureCommitRegion(): + """Data containing feature and commit regions.""" + def __init__( + self, feature: str, commit_hash: str, repo: str + ) -> None: + self.__feature = feature + self.__commit_hash = commit_hash + self.__repo = repo + + @staticmethod + def create_feature_commit_region( + raw_region_data: tp.Dict[str, tp.Any] + ) -> 'FeatureCommitRegion': + feature = raw_region_data["feature"] + commit_hash = raw_region_data["commit-hash"] + repo = raw_region_data["repo"] + return FeatureCommitRegion(feature, commit_hash, repo) + +class FeatureCommitRegionInstruction(): + """An instruction that has one or more feature-commit interaction and its location + in the project.""" + + def __init__( + self, instruction: str, location: str, feature_commit_regions: tp.List[str] + ) -> None: + self.__instruction = instruction + self.__location = location + self.__feature_commit_regions = sorted(feature_commit_regions) + + + @staticmethod + def create_feature_tainted_instruction( + raw_inst_entry: tp.Dict[str, tp.Any] + ) -> 'FeatureCommitRegionInstruction': + """Creates a `FeatureCommitRegionInstruction` entry from the corresponding + yaml document section.""" + instruction = str(raw_inst_entry['inst']) + location = str(raw_inst_entry['location']) + feature_commit_regions: tp.List[str] = [] + for fcr in raw_inst_entry['regions']: + feature_commit_regions.append(str(fcr)) + return FeatureCommitRegionInstruction(instruction, location, feature_commit_regions) + + + @property + def instruction(self) -> str: + """instruction containg commit and feature regions.""" + return self.__instruction + + @property + def location(self) -> str: + """Location of instruction in the project.""" + return self.__location + + @property + def feature_taints(self) -> tp.List[str]: + """List of commit and feature regions of this instruction.""" + return self.__feature_commit_regions + + def is_terminator(self) -> bool: + br_regex = re.compile(r'(br( i1 | label ))|(switch i\d{1,} )') + return br_regex.search(self.__instruction) is not None + + +class FeatureBlameResultFunctionEntry(): + """Collection of all feature commit region instructions for a specific + function.""" + + def __init__( + self, name: str, demangled_name: str, + feature_commit_region_insts: tp.List[FeatureCommitRegionInstruction] + ) -> None: + self.__name = name + self.__demangled_name = demangled_name + self.__feature_commit_region_insts = feature_commit_region_insts + + + @staticmethod + def create_feature_analysis_result_function_entry( + name: str, raw_function_entry: tp.Dict[str, tp.Any] + ) -> 'FeatureBlameResultFunctionEntry': + """Creates a `FeatureBlameResultFunctionEntry` from the corresponding + yaml document section.""" + demangled_name = str(raw_function_entry['demangled-name']) + inst_list: tp.List[FeatureCommitRegionInstruction] = [] + for raw_inst_entry in raw_function_entry['commit-feature-interaction-related-insts']: + inst_list.append( + FeatureCommitRegionInstruction. + create_feature_tainted_instruction(raw_inst_entry) + ) + return FeatureBlameResultFunctionEntry( + name, demangled_name, inst_list + ) + + + @property + def name(self) -> str: + """ + Name of the function. + + The name is mangled for C++ code, either with the itanium or windows + mangling schema. + """ + return self.__name + + @property + def demangled_name(self) -> str: + """Demangled name of the function.""" + return self.__demangled_name + + @property + def feature_commit_region_insts(self) -> tp.List[FeatureCommitRegionInstruction]: + """List of found feature commit region instructions.""" + return self.__feature_commit_region_insts + + +class FeatureBlameReportMetaData(): + """Provides extra meta data about llvm::Module, which was analyzed to + generate this ``FeatureBlameReport``.""" + + def __init__( + self, num_functions: int, num_instructions: int, + num_br_switch_insts: int + ) -> None: + self.__number_of_functions_in_module = num_functions + self.__number_of_instructions_in_module = num_instructions + self.__number_of_branch_and_switch_ints_in_module = num_br_switch_insts + + @property + def num_functions(self) -> int: + """Number of functions in the analyzed llvm::Module.""" + return self.__number_of_functions_in_module + + @property + def num_instructions(self) -> int: + """Number of instructions processed in the analyzed llvm::Module.""" + return self.__number_of_instructions_in_module + + @property + def num_br_switch_insts(self) -> int: + """Number of branch and switch instructions processed in the analyzed + llvm::Module.""" + return self.__number_of_branch_and_switch_ints_in_module + + @staticmethod + def create_feature_analysis_report_meta_data( + raw_document: tp.Dict[str, tp.Any] + ) -> 'FeatureBlameReportMetaData': + """Creates `FeatureBlameReportMetaData` from the corresponding yaml + document.""" + num_functions = int(raw_document['funcs-in-module']) + num_instructions = int(raw_document['insts-in-module']) + num_br_switch_insts = int(raw_document['br-switch-insts-in-module']) + + return FeatureBlameReportMetaData( + num_functions, num_instructions, num_br_switch_insts + ) + +class FeatureBlameReport(BaseReport, shorthand="FBR", file_type="yaml"): + """Data class that gives access to a loaded feature blame report.""" + + def __init__(self, path: Path) -> None: + super().__init__(path) + + with open(path, 'r') as stream: + documents = yaml.load_all(stream, Loader=yaml.CLoader) + version_header = VersionHeader(next(documents)) + version_header.raise_if_not_type("FeatureBlameReport") + version_header.raise_if_version_is_less_than(1) + + self.__meta_data = FeatureBlameReportMetaData \ + .create_feature_analysis_report_meta_data(next(documents)) + + self.__function_entries: tp.Dict[ + str, FeatureBlameResultFunctionEntry] = {} + raw_feature_analysis_report = next(documents) + for raw_func_entry in raw_feature_analysis_report['result-map']: + new_function_entry = ( + FeatureBlameResultFunctionEntry. + create_feature_analysis_result_function_entry( + raw_func_entry, + raw_feature_analysis_report['result-map'] + [raw_func_entry] + ) + ) + self.__function_entries[new_function_entry.name + ] = new_function_entry + + @property + def meta_data(self) -> FeatureBlameReportMetaData: + """Access the meta data that was gathered with the + ``FeatureBlameReport``.""" + return self.__meta_data + + @property + def function_entries( + self + ) -> tp.ValuesView[FeatureBlameResultFunctionEntry]: + """Iterate over all function entries.""" + return self.__function_entries.values() + + def get_feature_analysis_result_function_entry( + self, mangled_function_name: str + ) -> FeatureBlameResultFunctionEntry: + """ + Get the result entry for a specific function. + + Args: + mangled_function_name: mangled name of the function to look up + """ + return self.__function_entries[mangled_function_name] + + def get_feature_locations_dict(self) -> tp.Dict[str, tp.Set[str]]: + """Returns a dictionary that maps a feature name to a list of all + locations of tainted br and switch instructions.""" + \ No newline at end of file diff --git a/varats/varats/experiments/vara/feature_blame_experiment.py b/varats/varats/experiments/vara/feature_blame_experiment.py new file mode 100644 index 000000000..aa23bdc48 --- /dev/null +++ b/varats/varats/experiments/vara/feature_blame_experiment.py @@ -0,0 +1,73 @@ +"""Implements the base feature blame experiment, making it easier to create different +feature-blame experiments that have a similar experiment setup.""" + +import typing as tp + +from benchbuild import Project +from benchbuild.extensions import compiler, run, time +from benchbuild.utils import actions + +from varats.experiment.experiment_util import ( + VersionExperiment, + get_default_compile_error_wrapped, + PEErrorHandler, +) +from varats.experiment.wllvm import ( + RunWLLVM, + BCFileExtensions, + get_bc_cache_actions, +) +from varats.report.report import BaseReport + + +def setup_basic_feature_blame_experiment( + experiment: VersionExperiment, project: Project, + report_type: tp.Type[BaseReport] +) -> None: + """ + Setup the project for a feature blame experiment. + + - run time extensions + - compile time extensions + - prepare compiler + - configure C/CXX flags + """ + # Add the required runtime extensions to the project(s). + project.runtime_extension = run.RuntimeExtension(project, experiment) \ + << time.RunWithTime() + + # Add the required compiler extensions to the project(s). + project.compiler_extension = compiler.RunCompiler(project, experiment) \ + << RunWLLVM() \ + << run.WithTimeout() + + # Add own error handler to compile step. + project.compile = get_default_compile_error_wrapped( + experiment.get_handle(), project, report_type + ) + + # This c-flag is provided by VaRA and it suggests to use the git-blame + # annotation. + project.cflags += ["-fvara-GB", "-fvara-feature"] + + +def generate_basic_feature_blame_experiment_actions( + project: Project, + bc_file_extensions: tp.Optional[tp.List[BCFileExtensions]] = None, + extraction_error_handler: tp.Optional[PEErrorHandler] = None +) -> tp.List[actions.Step]: + """ + Generate the basic actions for a feature blame experiment. + + - handle caching of BC files + - compile project, if needed + + Args: + project: reference to the BB project + bc_file_extensions: list of bitcode file extensions (e.g. opt, no opt) + extraction_error_handler: handler to manage errors during the + extraction process + """ + return get_bc_cache_actions( + project, bc_file_extensions, extraction_error_handler + ) \ No newline at end of file diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py new file mode 100644 index 000000000..3687549c0 --- /dev/null +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -0,0 +1,138 @@ +""" +Implements the basic feature blame report experiment. + +The experiment analyses a project with VaRA's blame and feature analysis and generates a +FeatureBlameReport. +""" + +import typing as tp + +from benchbuild import Project +from benchbuild.utils import actions +from benchbuild.utils.cmd import opt +from benchbuild.utils.requirements import Requirement, SlurmMem + +import varats.experiments.vara.feature_blame_experiment as FBE +from varats.data.reports.feature_blame_report import FeatureBlameReport as FBR +from varats.experiment.experiment_util import ( + exec_func_with_pe_error_handler, + VersionExperiment, + ExperimentHandle, + wrap_unlimit_stack_size, + create_default_compiler_error_handler, + create_default_analysis_failure_handler, + create_new_success_result_filepath, +) +from varats.experiment.wllvm import get_cached_bc_file_path, BCFileExtensions +from varats.project.project_util import get_local_project_git_paths +from varats.project.varats_project import VProject +from varats.report.report import ReportSpecification + + +class FeatureBlameReportGeneration(actions.ProjectStep): # type: ignore + """Analyse a project with VaRA and generate a FeatureBlameReport.""" + + NAME = "FeatureBlameReportGeneration" + DESCRIPTION = "Analyses the bitcode with -vara-FBR of VaRA." + + project: VProject + + def __init__( + self, project: Project, experiment_handle: ExperimentHandle, + ): + super().__init__(project=project) + self.__experiment_handle = experiment_handle + + def __call__(self) -> actions.StepResult: + return self.analyze() + + def analyze(self) -> actions.StepResult: + """ + This step performs the actual analysis with the correct command line + flags. + + Flags used: + * -vara-FBR: to run a commit feature interaction report + * -yaml-report-outfile=: specify the path to store the results + """ + + for binary in self.project.binaries: + # Add to the user-defined path for saving the results of the + # analysis also the name and the unique id of the project of every + # run. + result_file = create_new_success_result_filepath( + self.__experiment_handle, FBR, self.project, binary + ) + + opt_params = [ + "--enable-new-pm=0", "-vara-PTFDD", "-vara-BD", "-vara-FBR", + "-vara-init-commits", "-vara-use-phasar", + f"-vara-report-outfile={result_file}", + get_cached_bc_file_path( + self.project, binary, [ + BCFileExtensions.NO_OPT, BCFileExtensions.TBAA, + BCFileExtensions.BLAME + ] + ) + ] + + run_cmd = opt[opt_params] + + run_cmd = wrap_unlimit_stack_size(run_cmd) + + exec_func_with_pe_error_handler( + run_cmd, + create_default_analysis_failure_handler( + self.__experiment_handle, self.project, FBR + ) + ) + + return actions.StepResult.OK + + +class FeatureBlameReportExperiment(VersionExperiment, shorthand="FBRE"): + """Generates a feature blame report of the project(s) specified in the call.""" + + NAME = "GenerateFeatureBlameReport" + + REPORT_SPEC = ReportSpecification(FBR) + REQUIREMENTS: tp.List[Requirement] = [SlurmMem("250G")] + + def actions_for_project( + self, project: VProject + ) -> tp.MutableSequence[actions.Step]: + """ + Returns the specified steps to run the project(s) specified in the call + in a fixed order. + + Args: + project: to analyze + """ + # Try, to build the project without optimizations to get more precise + # blame annotations. Note: this does not guarantee that a project is + # build without optimizations because the used build tool/script can + # still add optimizations flags after the experiment specified cflags. + project.cflags += ["-O1", "-Xclang", "-disable-llvm-optzns", "-g0"] + bc_file_extensions = [ + BCFileExtensions.NO_OPT, + BCFileExtensions.TBAA, + BCFileExtensions.BLAME, + ] + + FBE.setup_basic_feature_blame_experiment(self, project, FBR) + + analysis_actions = FBE.generate_basic_feature_blame_experiment_actions( + project, + bc_file_extensions, + extraction_error_handler=create_default_compiler_error_handler( + self.get_handle(), project, self.REPORT_SPEC.main_report + ) + ) + analysis_actions.append( + FeatureBlameReportGeneration( + project, self.get_handle() + ) + ) + analysis_actions.append(actions.Clean(project)) + + return analysis_actions \ No newline at end of file From 5d415a1478b0fccee545379a355646d10ea29bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Tue, 11 Apr 2023 19:00:13 +0200 Subject: [PATCH 02/26] add test_repo to test projects --- .../commit_feature_interactions_test_repo.py | 56 +++++++++++++++++++ varats/varats/tools/bb_config.py | 4 +- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py new file mode 100644 index 000000000..23f53c1d8 --- /dev/null +++ b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py @@ -0,0 +1,56 @@ +"""Projects in vara-test-repos used for testing the bug provider.""" +import typing as tp + +import benchbuild as bb +from plumbum import local + +from varats.project.project_domain import ProjectDomains +from varats.project.project_util import ( + ProjectBinaryWrapper, + wrap_paths_to_binaries, + BinaryType, +) +from varats.project.varats_project import VProject +from varats.ts_utils.project_sources import VaraTestRepoSource +from varats.utils.git_util import ShortCommitHash + +class CFI(VProject): # type: ignore + + NAME = 'CFI' # The name of the project + GROUP = 'cpp_projects' # The group this project belongs to + DOMAIN = ProjectDomains.TEST # The application domain of this project + + SOURCE = [ + VaraTestRepoSource( + project_name="CFI", + remote="https://github.com/sisteu56/commit-feature-interactions_test_repo.git", + local="CFI", + refspec="main", + limit=None + ) + ] + + test_files = [ + "main.cpp" + ] + + @property + def binaries(self) -> tp.List[ProjectBinaryWrapper]: + """Return a list of binaries generated by the project.""" + print("BINARIES\n") + return wrap_paths_to_binaries(["CFI"]) + + def run_tests(self) -> None: + print("RUN TESTS\n") + """ This function defines tests and benchmarks for the project. """ + pass + + def compile(self) -> None: + """ Contains instructions on how to build the project. """ + print("COMPILE\n") + source = local.path(self.source_of_primary) + + clang = bb.compiler.cxx(self) + with local.cwd(source): + for test_file in self.test_files: + bb.watch(clang)(test_file, "-o", test_file.replace('.cpp', '')) diff --git a/varats/varats/tools/bb_config.py b/varats/varats/tools/bb_config.py index eff48cada..eeb55078b 100644 --- a/varats/varats/tools/bb_config.py +++ b/varats/varats/tools/bb_config.py @@ -71,7 +71,8 @@ def update_projects( 'varats.projects.cpp_projects.poppler', 'varats.projects.cpp_projects.z3', 'varats.projects.cpp_projects.ect', - 'varats.projects.cpp_projects.lepton' + 'varats.projects.cpp_projects.lepton', + 'varats.projects.cpp_projects.commit_feature_interactions_test_repo' ] projects_conf.value[:] += [ 'varats.projects.cpp_projects.doxygen', 'varats.projects.cpp_projects' @@ -104,6 +105,7 @@ def update_experiments(bb_cfg: s.Configuration) -> None: 'varats.experiments.szz.pydriller_szz_experiment', 'varats.experiments.szz.szz_unleashed_experiment', 'varats.experiments.vara.agg_region_interaction_perf_runner', + 'varats.experiments.vara.feature_blame_report_experiment', 'varats.experiments.vara.blame_report_experiment', 'varats.experiments.vara.blame_verifier_experiment', 'varats.experiments.vara.commit_report_experiment', From 1405e8c4b854b486bf4322e9b8685ff9d9e53705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Wed, 12 Apr 2023 12:48:18 +0200 Subject: [PATCH 03/26] some nmae/type fixes --- .../data/reports/feature_blame_report.py | 32 +++++++++++++------ .../vara/feature_blame_experiment.py | 2 +- .../vara/feature_blame_report_experiment.py | 10 +++--- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index 0d886221d..d945d36d5 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -27,13 +27,25 @@ def create_feature_commit_region( commit_hash = raw_region_data["commit-hash"] repo = raw_region_data["repo"] return FeatureCommitRegion(feature, commit_hash, repo) + + @property + def feature(self) -> str: + return self.__feature + + @property + def commit_hash(self) -> str: + return self.__commit_hash + + @property + def repo(self) -> str: + return self.__repo class FeatureCommitRegionInstruction(): """An instruction that has one or more feature-commit interaction and its location in the project.""" def __init__( - self, instruction: str, location: str, feature_commit_regions: tp.List[str] + self, instruction: str, location: str, feature_commit_regions: tp.List[FeatureCommitRegion] ) -> None: self.__instruction = instruction self.__location = location @@ -48,9 +60,9 @@ def create_feature_tainted_instruction( yaml document section.""" instruction = str(raw_inst_entry['inst']) location = str(raw_inst_entry['location']) - feature_commit_regions: tp.List[str] = [] + feature_commit_regions: tp.List[FeatureCommitRegion] = [] for fcr in raw_inst_entry['regions']: - feature_commit_regions.append(str(fcr)) + feature_commit_regions.append(FeatureCommitRegion.create_feature_commit_region(fcr)) return FeatureCommitRegionInstruction(instruction, location, feature_commit_regions) @@ -65,7 +77,7 @@ def location(self) -> str: return self.__location @property - def feature_taints(self) -> tp.List[str]: + def feature_commit_regions(self) -> tp.List[str]: """List of commit and feature regions of this instruction.""" return self.__feature_commit_regions @@ -88,7 +100,7 @@ def __init__( @staticmethod - def create_feature_analysis_result_function_entry( + def create_feature_blame_result_function_entry( name: str, raw_function_entry: tp.Dict[str, tp.Any] ) -> 'FeatureBlameResultFunctionEntry': """Creates a `FeatureBlameResultFunctionEntry` from the corresponding @@ -185,13 +197,13 @@ def __init__(self, path: Path) -> None: self.__function_entries: tp.Dict[ str, FeatureBlameResultFunctionEntry] = {} - raw_feature_analysis_report = next(documents) - for raw_func_entry in raw_feature_analysis_report['result-map']: + raw_feature_blame_report = next(documents) + for raw_func_entry in raw_feature_blame_report['result-map']: new_function_entry = ( FeatureBlameResultFunctionEntry. - create_feature_analysis_result_function_entry( + create_feature_blame_result_function_entry( raw_func_entry, - raw_feature_analysis_report['result-map'] + raw_feature_blame_report['result-map'] [raw_func_entry] ) ) @@ -225,4 +237,4 @@ def get_feature_analysis_result_function_entry( def get_feature_locations_dict(self) -> tp.Dict[str, tp.Set[str]]: """Returns a dictionary that maps a feature name to a list of all locations of tainted br and switch instructions.""" - \ No newline at end of file + diff --git a/varats/varats/experiments/vara/feature_blame_experiment.py b/varats/varats/experiments/vara/feature_blame_experiment.py index aa23bdc48..5b6173d1c 100644 --- a/varats/varats/experiments/vara/feature_blame_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_experiment.py @@ -70,4 +70,4 @@ def generate_basic_feature_blame_experiment_actions( """ return get_bc_cache_actions( project, bc_file_extensions, extraction_error_handler - ) \ No newline at end of file + ) diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index 3687549c0..1b65f09a2 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -55,7 +55,7 @@ def analyze(self) -> actions.StepResult: * -vara-FBR: to run a commit feature interaction report * -yaml-report-outfile=: specify the path to store the results """ - + print("ANALYZE\n") for binary in self.project.binaries: # Add to the user-defined path for saving the results of the # analysis also the name and the unique id of the project of every @@ -63,7 +63,7 @@ def analyze(self) -> actions.StepResult: result_file = create_new_success_result_filepath( self.__experiment_handle, FBR, self.project, binary ) - + print("RESULT FILE: " + result_file + "\n") opt_params = [ "--enable-new-pm=0", "-vara-PTFDD", "-vara-BD", "-vara-FBR", "-vara-init-commits", "-vara-use-phasar", @@ -75,7 +75,7 @@ def analyze(self) -> actions.StepResult: ] ) ] - + print("OPT PARAMS: " + opt_params.__str__() + "\n") run_cmd = opt[opt_params] run_cmd = wrap_unlimit_stack_size(run_cmd) @@ -117,6 +117,7 @@ def actions_for_project( BCFileExtensions.NO_OPT, BCFileExtensions.TBAA, BCFileExtensions.BLAME, + BCFileExtensions.FEATURE ] FBE.setup_basic_feature_blame_experiment(self, project, FBR) @@ -135,4 +136,5 @@ def actions_for_project( ) analysis_actions.append(actions.Clean(project)) - return analysis_actions \ No newline at end of file + return analysis_actions + From bb752696e32fb94a1c300c4d92c9609a918f0d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Wed, 12 Apr 2023 12:48:52 +0200 Subject: [PATCH 04/26] correct binary? --- .../cpp_projects/commit_feature_interactions_test_repo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py index 23f53c1d8..09462adc6 100644 --- a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py +++ b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py @@ -38,7 +38,7 @@ class CFI(VProject): # type: ignore def binaries(self) -> tp.List[ProjectBinaryWrapper]: """Return a list of binaries generated by the project.""" print("BINARIES\n") - return wrap_paths_to_binaries(["CFI"]) + return wrap_paths_to_binaries(["main.ll"]) def run_tests(self) -> None: print("RUN TESTS\n") From 44a4669c2c278f3cf2a1e3fa2af6289210c54aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Thu, 13 Apr 2023 19:13:50 +0200 Subject: [PATCH 05/26] added print feature --- .../data/reports/feature_blame_report.py | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index d945d36d5..a07aaa7fd 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -28,6 +28,12 @@ def create_feature_commit_region( repo = raw_region_data["repo"] return FeatureCommitRegion(feature, commit_hash, repo) + def print(self) -> None: + print(" -FEATURE COMMIT REGION") + print(" -FEATURE: " + self.__feature) + print(" -COMMIT HASH: " + self.__commit_hash) + print(" -REPO: " + self.__repo) + @property def feature(self) -> str: return self.__feature @@ -49,7 +55,7 @@ def __init__( ) -> None: self.__instruction = instruction self.__location = location - self.__feature_commit_regions = sorted(feature_commit_regions) + self.__feature_commit_regions = feature_commit_regions @staticmethod @@ -66,6 +72,13 @@ def create_feature_tainted_instruction( return FeatureCommitRegionInstruction(instruction, location, feature_commit_regions) + def print(self) -> None: + print(" -FEATURE COMMIT REGION INSTRUCTION") + print(" -INSTRUCTION: " + self.__instruction) + print(" -LOCATION: " + self.__location) + for FCR in self.__feature_commit_regions: + FCR.print() + @property def instruction(self) -> str: """instruction containg commit and feature regions.""" @@ -116,6 +129,11 @@ def create_feature_blame_result_function_entry( name, demangled_name, inst_list ) + def print(self) -> None: + print(" -FEATURE BLAME RESULT FUNCTION ENTRY") + print(" -FUNCTION: " + self.demangled_name) + for FCRI in self.__feature_commit_region_insts: + FCRI.print() @property def name(self) -> str: @@ -210,6 +228,12 @@ def __init__(self, path: Path) -> None: self.__function_entries[new_function_entry.name ] = new_function_entry + + def print(self) -> None: + print("FEATURE BLAME REPORT") + for FE in self.__function_entries.values(): + FE.print() + @property def meta_data(self) -> FeatureBlameReportMetaData: """Access the meta data that was gathered with the From fd99f6ca2e22a7b88c4bf6f819d24a1511fca791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Thu, 13 Apr 2023 19:14:45 +0200 Subject: [PATCH 06/26] change config --- .../commit_feature_interactions_test_repo.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py index 09462adc6..284052cd9 100644 --- a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py +++ b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py @@ -4,6 +4,7 @@ import benchbuild as bb from plumbum import local +from varats.paper.paper_config import PaperConfigSpecificGit from varats.project.project_domain import ProjectDomains from varats.project.project_util import ( ProjectBinaryWrapper, @@ -11,7 +12,6 @@ BinaryType, ) from varats.project.varats_project import VProject -from varats.ts_utils.project_sources import VaraTestRepoSource from varats.utils.git_util import ShortCommitHash class CFI(VProject): # type: ignore @@ -21,12 +21,13 @@ class CFI(VProject): # type: ignore DOMAIN = ProjectDomains.TEST # The application domain of this project SOURCE = [ - VaraTestRepoSource( + PaperConfigSpecificGit( project_name="CFI", remote="https://github.com/sisteu56/commit-feature-interactions_test_repo.git", local="CFI", refspec="main", - limit=None + limit=None, + shallow=False ) ] @@ -37,17 +38,15 @@ class CFI(VProject): # type: ignore @property def binaries(self) -> tp.List[ProjectBinaryWrapper]: """Return a list of binaries generated by the project.""" - print("BINARIES\n") - return wrap_paths_to_binaries(["main.ll"]) + return wrap_paths_to_binaries(binaries=[("main", BinaryType.EXECUTABLE)]) + def run_tests(self) -> None: - print("RUN TESTS\n") """ This function defines tests and benchmarks for the project. """ pass def compile(self) -> None: """ Contains instructions on how to build the project. """ - print("COMPILE\n") source = local.path(self.source_of_primary) clang = bb.compiler.cxx(self) From 11bf7baf20586e2cc71c83c1f4c06ac0937c7a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Thu, 13 Apr 2023 19:15:30 +0200 Subject: [PATCH 07/26] create bc with clang --- .../vara/feature_blame_experiment.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/varats/varats/experiments/vara/feature_blame_experiment.py b/varats/varats/experiments/vara/feature_blame_experiment.py index 5b6173d1c..c052c03fc 100644 --- a/varats/varats/experiments/vara/feature_blame_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_experiment.py @@ -5,11 +5,17 @@ from benchbuild import Project from benchbuild.extensions import compiler, run, time +from benchbuild.utils.cmd import clang +import os from benchbuild.utils import actions +from varats.data.reports.feature_blame_report import FeatureBlameReport as FBR from varats.experiment.experiment_util import ( + exec_func_with_pe_error_handler, VersionExperiment, get_default_compile_error_wrapped, + wrap_unlimit_stack_size, + create_default_analysis_failure_handler, PEErrorHandler, ) from varats.experiment.wllvm import ( @@ -50,6 +56,29 @@ def setup_basic_feature_blame_experiment( # annotation. project.cflags += ["-fvara-GB", "-fvara-feature"] + for binary in project.binaries: + # Add to the user-defined path for saving the results of the + # analysis also the name and the unique id of the project of every + # run. + + current_dir = os.getcwd() + file = current_dir + "/tmp/" + project.name + "/" + "main" + + clang_params = [ + "-fvara-GB", "-fvara-feature", "-S", "-emit-llvm", file + ".cpp" ,"-c", "-o", file + ".bc" + ] + + clang_cmd = clang[clang_params] + + clang_cmd = wrap_unlimit_stack_size(clang_cmd) + + exec_func_with_pe_error_handler( + clang_cmd, + create_default_analysis_failure_handler( + experiment.get_handle(), project, FBR + ) + ) + def generate_basic_feature_blame_experiment_actions( project: Project, From 5fe3a664c363b372474523661201afa6fc0473bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Thu, 13 Apr 2023 19:16:08 +0200 Subject: [PATCH 08/26] print out FBR --- .../vara/feature_blame_report_experiment.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index 1b65f09a2..949006d79 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -6,6 +6,7 @@ """ import typing as tp +from pathlib import Path from benchbuild import Project from benchbuild.utils import actions @@ -55,7 +56,6 @@ def analyze(self) -> actions.StepResult: * -vara-FBR: to run a commit feature interaction report * -yaml-report-outfile=: specify the path to store the results """ - print("ANALYZE\n") for binary in self.project.binaries: # Add to the user-defined path for saving the results of the # analysis also the name and the unique id of the project of every @@ -63,7 +63,7 @@ def analyze(self) -> actions.StepResult: result_file = create_new_success_result_filepath( self.__experiment_handle, FBR, self.project, binary ) - print("RESULT FILE: " + result_file + "\n") + opt_params = [ "--enable-new-pm=0", "-vara-PTFDD", "-vara-BD", "-vara-FBR", "-vara-init-commits", "-vara-use-phasar", @@ -71,11 +71,11 @@ def analyze(self) -> actions.StepResult: get_cached_bc_file_path( self.project, binary, [ BCFileExtensions.NO_OPT, BCFileExtensions.TBAA, - BCFileExtensions.BLAME + BCFileExtensions.BLAME, BCFileExtensions.FEATURE ] ) ] - print("OPT PARAMS: " + opt_params.__str__() + "\n") + run_cmd = opt[opt_params] run_cmd = wrap_unlimit_stack_size(run_cmd) @@ -87,6 +87,10 @@ def analyze(self) -> actions.StepResult: ) ) + test_fbr = FBR(path=Path(result_file.__str__())) + + test_fbr.print() + return actions.StepResult.OK From 2603afc66944b0b93f206ae5c9729cb30ec19cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Fri, 14 Apr 2023 15:50:35 +0200 Subject: [PATCH 09/26] remove unnecessary code --- .../vara/feature_blame_experiment.py | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/varats/varats/experiments/vara/feature_blame_experiment.py b/varats/varats/experiments/vara/feature_blame_experiment.py index c052c03fc..f10829fdb 100644 --- a/varats/varats/experiments/vara/feature_blame_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_experiment.py @@ -5,17 +5,12 @@ from benchbuild import Project from benchbuild.extensions import compiler, run, time -from benchbuild.utils.cmd import clang -import os from benchbuild.utils import actions from varats.data.reports.feature_blame_report import FeatureBlameReport as FBR from varats.experiment.experiment_util import ( - exec_func_with_pe_error_handler, VersionExperiment, get_default_compile_error_wrapped, - wrap_unlimit_stack_size, - create_default_analysis_failure_handler, PEErrorHandler, ) from varats.experiment.wllvm import ( @@ -56,29 +51,6 @@ def setup_basic_feature_blame_experiment( # annotation. project.cflags += ["-fvara-GB", "-fvara-feature"] - for binary in project.binaries: - # Add to the user-defined path for saving the results of the - # analysis also the name and the unique id of the project of every - # run. - - current_dir = os.getcwd() - file = current_dir + "/tmp/" + project.name + "/" + "main" - - clang_params = [ - "-fvara-GB", "-fvara-feature", "-S", "-emit-llvm", file + ".cpp" ,"-c", "-o", file + ".bc" - ] - - clang_cmd = clang[clang_params] - - clang_cmd = wrap_unlimit_stack_size(clang_cmd) - - exec_func_with_pe_error_handler( - clang_cmd, - create_default_analysis_failure_handler( - experiment.get_handle(), project, FBR - ) - ) - def generate_basic_feature_blame_experiment_actions( project: Project, From a1a9f4c69d620cece65e8cb1ec45cda7591f8b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Fri, 14 Apr 2023 15:50:55 +0200 Subject: [PATCH 10/26] minor cleanup --- .../experiments/vara/feature_blame_report_experiment.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index 949006d79..c58e47983 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -1,12 +1,10 @@ """ Implements the basic feature blame report experiment. - The experiment analyses a project with VaRA's blame and feature analysis and generates a FeatureBlameReport. """ import typing as tp -from pathlib import Path from benchbuild import Project from benchbuild.utils import actions @@ -51,7 +49,6 @@ def analyze(self) -> actions.StepResult: """ This step performs the actual analysis with the correct command line flags. - Flags used: * -vara-FBR: to run a commit feature interaction report * -yaml-report-outfile=: specify the path to store the results @@ -87,7 +84,7 @@ def analyze(self) -> actions.StepResult: ) ) - test_fbr = FBR(path=Path(result_file.__str__())) + test_fbr = FBR(path=result_file.full_path()) test_fbr.print() @@ -108,7 +105,6 @@ def actions_for_project( """ Returns the specified steps to run the project(s) specified in the call in a fixed order. - Args: project: to analyze """ @@ -140,5 +136,4 @@ def actions_for_project( ) analysis_actions.append(actions.Clean(project)) - return analysis_actions - + return analysis_actions \ No newline at end of file From edc3c4bb0c1129cc27018ffb13e0605946e883bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Fri, 14 Apr 2023 15:51:18 +0200 Subject: [PATCH 11/26] added new file --- .../cpp_projects/commit_feature_interactions_test_repo.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py index 284052cd9..2b4db6a74 100644 --- a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py +++ b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py @@ -32,14 +32,13 @@ class CFI(VProject): # type: ignore ] test_files = [ - "main.cpp" + "main.cpp", "helper.cpp" ] @property def binaries(self) -> tp.List[ProjectBinaryWrapper]: """Return a list of binaries generated by the project.""" - return wrap_paths_to_binaries(binaries=[("main", BinaryType.EXECUTABLE)]) - + return wrap_paths_to_binaries(binaries=[("main", BinaryType.EXECUTABLE),("helper", BinaryType.EXECUTABLE)]) def run_tests(self) -> None: """ This function defines tests and benchmarks for the project. """ @@ -48,7 +47,6 @@ def run_tests(self) -> None: def compile(self) -> None: """ Contains instructions on how to build the project. """ source = local.path(self.source_of_primary) - clang = bb.compiler.cxx(self) with local.cwd(source): for test_file in self.test_files: From 071b7c3a905358c7b59d8bf05f86a3087320ff70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Mon, 17 Apr 2023 12:26:57 +0200 Subject: [PATCH 12/26] fixes for pre-commit hook --- .gitignore | 1 + .../data/reports/feature_blame_report.py | 81 ++++++++++--------- .../vara/feature_blame_experiment.py | 4 +- .../vara/feature_blame_report_experiment.py | 42 +++++----- .../commit_feature_interactions_test_repo.py | 24 +++--- 5 files changed, 80 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index ad4f71409..281776467 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ build/ .tox/ docs/source/.varats.yaml docs/**/*.inc +pre-commit-env diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index a07aaa7fd..475f1a466 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -12,9 +12,8 @@ class FeatureCommitRegion(): """Data containing feature and commit regions.""" - def __init__( - self, feature: str, commit_hash: str, repo: str - ) -> None: + + def __init__(self, feature: str, commit_hash: str, repo: str) -> None: self.__feature = feature self.__commit_hash = commit_hash self.__repo = repo @@ -23,12 +22,15 @@ def __init__( def create_feature_commit_region( raw_region_data: tp.Dict[str, tp.Any] ) -> 'FeatureCommitRegion': + """Creates a 'FeatureCommitRegion' from the corresponding yaml document + section.""" feature = raw_region_data["feature"] commit_hash = raw_region_data["commit-hash"] repo = raw_region_data["repo"] return FeatureCommitRegion(feature, commit_hash, repo) - + def print(self) -> None: + """'FeatureCommitRegion' prints itself.""" print(" -FEATURE COMMIT REGION") print(" -FEATURE: " + self.__feature) print(" -COMMIT HASH: " + self.__commit_hash) @@ -37,47 +39,52 @@ def print(self) -> None: @property def feature(self) -> str: return self.__feature - + @property def commit_hash(self) -> str: return self.__commit_hash - + @property def repo(self) -> str: return self.__repo + class FeatureCommitRegionInstruction(): - """An instruction that has one or more feature-commit interaction and its location - in the project.""" + """An instruction that has one or more feature-commit interaction and its + location in the project.""" def __init__( - self, instruction: str, location: str, feature_commit_regions: tp.List[FeatureCommitRegion] + self, instruction: str, location: str, + feature_commit_regions: tp.List[FeatureCommitRegion] ) -> None: self.__instruction = instruction self.__location = location self.__feature_commit_regions = feature_commit_regions - @staticmethod def create_feature_tainted_instruction( raw_inst_entry: tp.Dict[str, tp.Any] ) -> 'FeatureCommitRegionInstruction': - """Creates a `FeatureCommitRegionInstruction` entry from the corresponding - yaml document section.""" + """Creates a `FeatureCommitRegionInstruction` entry from the + corresponding yaml document section.""" instruction = str(raw_inst_entry['inst']) location = str(raw_inst_entry['location']) feature_commit_regions: tp.List[FeatureCommitRegion] = [] for fcr in raw_inst_entry['regions']: - feature_commit_regions.append(FeatureCommitRegion.create_feature_commit_region(fcr)) - return FeatureCommitRegionInstruction(instruction, location, feature_commit_regions) - + feature_commit_regions.append( + FeatureCommitRegion.create_feature_commit_region(fcr) + ) + return FeatureCommitRegionInstruction( + instruction, location, feature_commit_regions + ) def print(self) -> None: + """'FeatureCommitRegionInstruction' prints itself.""" print(" -FEATURE COMMIT REGION INSTRUCTION") print(" -INSTRUCTION: " + self.__instruction) print(" -LOCATION: " + self.__location) - for FCR in self.__feature_commit_regions: - FCR.print() + for FeatureCommitRegion in self.__feature_commit_regions: + FeatureCommitRegion.print() @property def instruction(self) -> str: @@ -90,7 +97,7 @@ def location(self) -> str: return self.__location @property - def feature_commit_regions(self) -> tp.List[str]: + def feature_commit_regions(self) -> tp.List[FeatureCommitRegion]: """List of commit and feature regions of this instruction.""" return self.__feature_commit_regions @@ -111,7 +118,6 @@ def __init__( self.__demangled_name = demangled_name self.__feature_commit_region_insts = feature_commit_region_insts - @staticmethod def create_feature_blame_result_function_entry( name: str, raw_function_entry: tp.Dict[str, tp.Any] @@ -120,20 +126,20 @@ def create_feature_blame_result_function_entry( yaml document section.""" demangled_name = str(raw_function_entry['demangled-name']) inst_list: tp.List[FeatureCommitRegionInstruction] = [] - for raw_inst_entry in raw_function_entry['commit-feature-interaction-related-insts']: + for raw_inst_entry in raw_function_entry[ + 'commit-feature-interaction-related-insts']: inst_list.append( FeatureCommitRegionInstruction. create_feature_tainted_instruction(raw_inst_entry) ) - return FeatureBlameResultFunctionEntry( - name, demangled_name, inst_list - ) - + return FeatureBlameResultFunctionEntry(name, demangled_name, inst_list) + def print(self) -> None: + """'FeatureBlameResultFunctionEntry' prints itself.""" print(" -FEATURE BLAME RESULT FUNCTION ENTRY") print(" -FUNCTION: " + self.demangled_name) - for FCRI in self.__feature_commit_region_insts: - FCRI.print() + for FeatureCommitRegionInstruction in self.__feature_commit_region_insts: + FeatureCommitRegionInstruction.print() @property def name(self) -> str: @@ -151,7 +157,9 @@ def demangled_name(self) -> str: return self.__demangled_name @property - def feature_commit_region_insts(self) -> tp.List[FeatureCommitRegionInstruction]: + def feature_commit_region_insts( + self + ) -> tp.List[FeatureCommitRegionInstruction]: """List of found feature commit region instructions.""" return self.__feature_commit_region_insts @@ -198,6 +206,7 @@ def create_feature_analysis_report_meta_data( num_functions, num_instructions, num_br_switch_insts ) + class FeatureBlameReport(BaseReport, shorthand="FBR", file_type="yaml"): """Data class that gives access to a loaded feature blame report.""" @@ -212,7 +221,7 @@ def __init__(self, path: Path) -> None: self.__meta_data = FeatureBlameReportMetaData \ .create_feature_analysis_report_meta_data(next(documents)) - + self.__function_entries: tp.Dict[ str, FeatureBlameResultFunctionEntry] = {} raw_feature_blame_report = next(documents) @@ -221,32 +230,31 @@ def __init__(self, path: Path) -> None: FeatureBlameResultFunctionEntry. create_feature_blame_result_function_entry( raw_func_entry, - raw_feature_blame_report['result-map'] - [raw_func_entry] + raw_feature_blame_report['result-map'][raw_func_entry] ) ) self.__function_entries[new_function_entry.name ] = new_function_entry - def print(self) -> None: + """'FeatureBlameReport' prints itself.""" print("FEATURE BLAME REPORT") - for FE in self.__function_entries.values(): - FE.print() + for FeatureBlameResultFunctionEntry in self.__function_entries.values(): + FeatureBlameResultFunctionEntry.print() @property def meta_data(self) -> FeatureBlameReportMetaData: """Access the meta data that was gathered with the ``FeatureBlameReport``.""" return self.__meta_data - + @property def function_entries( self ) -> tp.ValuesView[FeatureBlameResultFunctionEntry]: """Iterate over all function entries.""" return self.__function_entries.values() - + def get_feature_analysis_result_function_entry( self, mangled_function_name: str ) -> FeatureBlameResultFunctionEntry: @@ -258,7 +266,6 @@ def get_feature_analysis_result_function_entry( """ return self.__function_entries[mangled_function_name] - def get_feature_locations_dict(self) -> tp.Dict[str, tp.Set[str]]: + # def get_feature_locations_dict(self) -> tp.Dict[str, tp.Set[str]]: """Returns a dictionary that maps a feature name to a list of all locations of tainted br and switch instructions.""" - diff --git a/varats/varats/experiments/vara/feature_blame_experiment.py b/varats/varats/experiments/vara/feature_blame_experiment.py index f10829fdb..aeb1bd1cd 100644 --- a/varats/varats/experiments/vara/feature_blame_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_experiment.py @@ -1,5 +1,5 @@ -"""Implements the base feature blame experiment, making it easier to create different -feature-blame experiments that have a similar experiment setup.""" +"""Implements the base feature blame experiment, making it easier to create +different feature-blame experiments that have a similar experiment setup.""" import typing as tp diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index c58e47983..40337f55f 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -1,7 +1,8 @@ """ Implements the basic feature blame report experiment. -The experiment analyses a project with VaRA's blame and feature analysis and generates a -FeatureBlameReport. + +The experiment analyses a project with VaRA's blame and feature analysis and +generates a FeatureBlameReport. """ import typing as tp @@ -23,7 +24,6 @@ create_new_success_result_filepath, ) from varats.experiment.wllvm import get_cached_bc_file_path, BCFileExtensions -from varats.project.project_util import get_local_project_git_paths from varats.project.varats_project import VProject from varats.report.report import ReportSpecification @@ -37,21 +37,23 @@ class FeatureBlameReportGeneration(actions.ProjectStep): # type: ignore project: VProject def __init__( - self, project: Project, experiment_handle: ExperimentHandle, + self, + project: Project, + experiment_handle: ExperimentHandle, ): super().__init__(project=project) self.__experiment_handle = experiment_handle def __call__(self) -> actions.StepResult: return self.analyze() - + def analyze(self) -> actions.StepResult: """ This step performs the actual analysis with the correct command line - flags. - Flags used: - * -vara-FBR: to run a commit feature interaction report - * -yaml-report-outfile=: specify the path to store the results + flags. Flags used: + + * -vara-FBR: to run a commit feature interaction report + * -yaml-report-outfile=: specify the path to store the results """ for binary in self.project.binaries: # Add to the user-defined path for saving the results of the @@ -63,7 +65,7 @@ def analyze(self) -> actions.StepResult: opt_params = [ "--enable-new-pm=0", "-vara-PTFDD", "-vara-BD", "-vara-FBR", - "-vara-init-commits", "-vara-use-phasar", + "-vara-init-commits", "-vara-use-phasar", f"-vara-report-outfile={result_file}", get_cached_bc_file_path( self.project, binary, [ @@ -72,7 +74,7 @@ def analyze(self) -> actions.StepResult: ] ) ] - + run_cmd = opt[opt_params] run_cmd = wrap_unlimit_stack_size(run_cmd) @@ -89,10 +91,11 @@ def analyze(self) -> actions.StepResult: test_fbr.print() return actions.StepResult.OK - + class FeatureBlameReportExperiment(VersionExperiment, shorthand="FBRE"): - """Generates a feature blame report of the project(s) specified in the call.""" + """Generates a feature blame report of the project(s) specified in the + call.""" NAME = "GenerateFeatureBlameReport" @@ -105,6 +108,7 @@ def actions_for_project( """ Returns the specified steps to run the project(s) specified in the call in a fixed order. + Args: project: to analyze """ @@ -114,10 +118,8 @@ def actions_for_project( # still add optimizations flags after the experiment specified cflags. project.cflags += ["-O1", "-Xclang", "-disable-llvm-optzns", "-g0"] bc_file_extensions = [ - BCFileExtensions.NO_OPT, - BCFileExtensions.TBAA, - BCFileExtensions.BLAME, - BCFileExtensions.FEATURE + BCFileExtensions.NO_OPT, BCFileExtensions.TBAA, + BCFileExtensions.BLAME, BCFileExtensions.FEATURE ] FBE.setup_basic_feature_blame_experiment(self, project, FBR) @@ -130,10 +132,8 @@ def actions_for_project( ) ) analysis_actions.append( - FeatureBlameReportGeneration( - project, self.get_handle() - ) + FeatureBlameReportGeneration(project, self.get_handle()) ) analysis_actions.append(actions.Clean(project)) - return analysis_actions \ No newline at end of file + return analysis_actions diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py index 2b4db6a74..6aa4d40be 100644 --- a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py +++ b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py @@ -12,18 +12,18 @@ BinaryType, ) from varats.project.varats_project import VProject -from varats.utils.git_util import ShortCommitHash -class CFI(VProject): # type: ignore - NAME = 'CFI' # The name of the project +class CFI(VProject): # type: ignore + """test repo for commit-feature interactions.""" + NAME = 'CFI' # The name of the project GROUP = 'cpp_projects' # The group this project belongs to - DOMAIN = ProjectDomains.TEST # The application domain of this project + DOMAIN = ProjectDomains.TEST # The application domain of this project SOURCE = [ PaperConfigSpecificGit( project_name="CFI", - remote="https://github.com/sisteu56/commit-feature-interactions_test_repo.git", + remote="https://github.com/sisteu56/cfi_test_repo.git", local="CFI", refspec="main", limit=None, @@ -31,21 +31,21 @@ class CFI(VProject): # type: ignore ) ] - test_files = [ - "main.cpp", "helper.cpp" - ] + test_files = ["main.cpp", "helper.cpp"] @property def binaries(self) -> tp.List[ProjectBinaryWrapper]: """Return a list of binaries generated by the project.""" - return wrap_paths_to_binaries(binaries=[("main", BinaryType.EXECUTABLE),("helper", BinaryType.EXECUTABLE)]) + return wrap_paths_to_binaries( + binaries=[("main", BinaryType.EXECUTABLE + ), ("helper", BinaryType.EXECUTABLE)] + ) def run_tests(self) -> None: - """ This function defines tests and benchmarks for the project. """ - pass + """This function defines tests and benchmarks for the project.""" def compile(self) -> None: - """ Contains instructions on how to build the project. """ + """Contains instructions on how to build the project.""" source = local.path(self.source_of_primary) clang = bb.compiler.cxx(self) with local.cwd(source): From c52af3e5b3eaf4ad6ecb2a26037122eb1736d29a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Mon, 17 Apr 2023 18:34:54 +0200 Subject: [PATCH 13/26] review changes --- .../data/reports/feature_blame_report.py | 91 +++++++------------ .../commit_feature_interactions_test_repo.py | 4 +- 2 files changed, 33 insertions(+), 62 deletions(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index 475f1a466..a168621fc 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -3,63 +3,25 @@ import re import typing as tp from pathlib import Path - +from varats.utils.git_util import CommitRepoPair import yaml from varats.base.version_header import VersionHeader from varats.report.report import BaseReport -class FeatureCommitRegion(): - """Data containing feature and commit regions.""" - - def __init__(self, feature: str, commit_hash: str, repo: str) -> None: - self.__feature = feature - self.__commit_hash = commit_hash - self.__repo = repo - - @staticmethod - def create_feature_commit_region( - raw_region_data: tp.Dict[str, tp.Any] - ) -> 'FeatureCommitRegion': - """Creates a 'FeatureCommitRegion' from the corresponding yaml document - section.""" - feature = raw_region_data["feature"] - commit_hash = raw_region_data["commit-hash"] - repo = raw_region_data["repo"] - return FeatureCommitRegion(feature, commit_hash, repo) - - def print(self) -> None: - """'FeatureCommitRegion' prints itself.""" - print(" -FEATURE COMMIT REGION") - print(" -FEATURE: " + self.__feature) - print(" -COMMIT HASH: " + self.__commit_hash) - print(" -REPO: " + self.__repo) - - @property - def feature(self) -> str: - return self.__feature - - @property - def commit_hash(self) -> str: - return self.__commit_hash - - @property - def repo(self) -> str: - return self.__repo - - class FeatureCommitRegionInstruction(): """An instruction that has one or more feature-commit interaction and its location in the project.""" def __init__( self, instruction: str, location: str, - feature_commit_regions: tp.List[FeatureCommitRegion] + features: tp.List[str], commit: CommitRepoPair ) -> None: self.__instruction = instruction self.__location = location - self.__feature_commit_regions = feature_commit_regions + self.__features = features + self.__commit = commit @staticmethod def create_feature_tainted_instruction( @@ -69,13 +31,13 @@ def create_feature_tainted_instruction( corresponding yaml document section.""" instruction = str(raw_inst_entry['inst']) location = str(raw_inst_entry['location']) - feature_commit_regions: tp.List[FeatureCommitRegion] = [] - for fcr in raw_inst_entry['regions']: - feature_commit_regions.append( - FeatureCommitRegion.create_feature_commit_region(fcr) - ) + features: tp.List[str] = raw_inst_entry['feature-regions'] + crp: tp.Dict[str] = raw_inst_entry['commit-region'] + commit: CommitRepoPair = CommitRepoPair( + crp['commit'], + crp['repository']) return FeatureCommitRegionInstruction( - instruction, location, feature_commit_regions + instruction, location, features, commit ) def print(self) -> None: @@ -83,8 +45,12 @@ def print(self) -> None: print(" -FEATURE COMMIT REGION INSTRUCTION") print(" -INSTRUCTION: " + self.__instruction) print(" -LOCATION: " + self.__location) - for FeatureCommitRegion in self.__feature_commit_regions: - FeatureCommitRegion.print() + print(" -FEATURE REGIONS: ") + for feature_region in self.__features: + print(" -" + feature_region) + print(" -COMMIT: ") + print(" -HASH: " + self.__commit.commit_hash) + print(" -REPO: " + self.__commit.repository_name) @property def instruction(self) -> str: @@ -97,9 +63,14 @@ def location(self) -> str: return self.__location @property - def feature_commit_regions(self) -> tp.List[FeatureCommitRegion]: - """List of commit and feature regions of this instruction.""" - return self.__feature_commit_regions + def features(self) -> tp.List[str]: + """List of feature regions of this instruction.""" + return self.__features + + @property + def commit_region(self) -> CommitRepoPair: + """commit region of this instruction.""" + return self.__commit def is_terminator(self) -> bool: br_regex = re.compile(r'(br( i1 | label ))|(switch i\d{1,} )') @@ -138,8 +109,8 @@ def print(self) -> None: """'FeatureBlameResultFunctionEntry' prints itself.""" print(" -FEATURE BLAME RESULT FUNCTION ENTRY") print(" -FUNCTION: " + self.demangled_name) - for FeatureCommitRegionInstruction in self.__feature_commit_region_insts: - FeatureCommitRegionInstruction.print() + for feature_commit_region_instruction in self.__feature_commit_region_insts: + feature_commit_region_instruction.print() @property def name(self) -> str: @@ -239,8 +210,8 @@ def __init__(self, path: Path) -> None: def print(self) -> None: """'FeatureBlameReport' prints itself.""" print("FEATURE BLAME REPORT") - for FeatureBlameResultFunctionEntry in self.__function_entries.values(): - FeatureBlameResultFunctionEntry.print() + for feature_blame_result_function_entry in self.__function_entries.values(): + feature_blame_result_function_entry.print() @property def meta_data(self) -> FeatureBlameReportMetaData: @@ -266,6 +237,6 @@ def get_feature_analysis_result_function_entry( """ return self.__function_entries[mangled_function_name] - # def get_feature_locations_dict(self) -> tp.Dict[str, tp.Set[str]]: - """Returns a dictionary that maps a feature name to a list of all - locations of tainted br and switch instructions.""" + # def get_feature_locations_dict(self) -> tp.Dict[str, tp.Set[str]]: + # """Returns a dictionary that maps a feature name to a list of all + # locations of tainted br and switch instructions.""" diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py index 6aa4d40be..5f064d870 100644 --- a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py +++ b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py @@ -33,8 +33,8 @@ class CFI(VProject): # type: ignore test_files = ["main.cpp", "helper.cpp"] - @property - def binaries(self) -> tp.List[ProjectBinaryWrapper]: + @staticmethod + def binaries_for_revision(self) -> tp.List[ProjectBinaryWrapper]: """Return a list of binaries generated by the project.""" return wrap_paths_to_binaries( binaries=[("main", BinaryType.EXECUTABLE From 0deac4e66e07477f88fb8006b513f5554b6829f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Tue, 18 Apr 2023 15:01:38 +0200 Subject: [PATCH 14/26] fixes by pre-commit --- .../data/reports/feature_blame_report.py | 27 ++++++++++--------- .../vara/feature_blame_experiment.py | 1 - .../commit_feature_interactions_test_repo.py | 5 +++- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index a168621fc..cbeb5c175 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -3,11 +3,12 @@ import re import typing as tp from pathlib import Path -from varats.utils.git_util import CommitRepoPair + import yaml from varats.base.version_header import VersionHeader from varats.report.report import BaseReport +from varats.utils.git_util import CommitRepoPair class FeatureCommitRegionInstruction(): @@ -15,8 +16,8 @@ class FeatureCommitRegionInstruction(): location in the project.""" def __init__( - self, instruction: str, location: str, - features: tp.List[str], commit: CommitRepoPair + self, instruction: str, location: str, features: tp.List[str], + commit: CommitRepoPair ) -> None: self.__instruction = instruction self.__location = location @@ -31,11 +32,11 @@ def create_feature_tainted_instruction( corresponding yaml document section.""" instruction = str(raw_inst_entry['inst']) location = str(raw_inst_entry['location']) - features: tp.List[str] = raw_inst_entry['feature-regions'] - crp: tp.Dict[str] = raw_inst_entry['commit-region'] + features: tp.List[str] = raw_inst_entry['features'] + crp: tp.Dict[str] = raw_inst_entry['commit'] commit: CommitRepoPair = CommitRepoPair( - crp['commit'], - crp['repository']) + crp['commit'], crp['repository'] + ) return FeatureCommitRegionInstruction( instruction, location, features, commit ) @@ -109,8 +110,8 @@ def print(self) -> None: """'FeatureBlameResultFunctionEntry' prints itself.""" print(" -FEATURE BLAME RESULT FUNCTION ENTRY") print(" -FUNCTION: " + self.demangled_name) - for feature_commit_region_instruction in self.__feature_commit_region_insts: - feature_commit_region_instruction.print() + for feature_commit_region_inst in self.__feature_commit_region_insts: + feature_commit_region_inst.print() @property def name(self) -> str: @@ -210,8 +211,8 @@ def __init__(self, path: Path) -> None: def print(self) -> None: """'FeatureBlameReport' prints itself.""" print("FEATURE BLAME REPORT") - for feature_blame_result_function_entry in self.__function_entries.values(): - feature_blame_result_function_entry.print() + for feature_blame_result_func_entr in self.__function_entries.values(): + feature_blame_result_func_entr.print() @property def meta_data(self) -> FeatureBlameReportMetaData: @@ -238,5 +239,5 @@ def get_feature_analysis_result_function_entry( return self.__function_entries[mangled_function_name] # def get_feature_locations_dict(self) -> tp.Dict[str, tp.Set[str]]: - # """Returns a dictionary that maps a feature name to a list of all - # locations of tainted br and switch instructions.""" + # """Returns a dictionary that maps a feature name to a list of all + # locations of tainted br and switch instructions.""" diff --git a/varats/varats/experiments/vara/feature_blame_experiment.py b/varats/varats/experiments/vara/feature_blame_experiment.py index aeb1bd1cd..cedd1be6f 100644 --- a/varats/varats/experiments/vara/feature_blame_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_experiment.py @@ -7,7 +7,6 @@ from benchbuild.extensions import compiler, run, time from benchbuild.utils import actions -from varats.data.reports.feature_blame_report import FeatureBlameReport as FBR from varats.experiment.experiment_util import ( VersionExperiment, get_default_compile_error_wrapped, diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py index 5f064d870..83c4a531e 100644 --- a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py +++ b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py @@ -12,6 +12,7 @@ BinaryType, ) from varats.project.varats_project import VProject +from varats.utils.git_util import ShortCommitHash class CFI(VProject): # type: ignore @@ -34,7 +35,9 @@ class CFI(VProject): # type: ignore test_files = ["main.cpp", "helper.cpp"] @staticmethod - def binaries_for_revision(self) -> tp.List[ProjectBinaryWrapper]: + def binaries_for_revision( + revision: ShortCommitHash + ) -> tp.List[ProjectBinaryWrapper]: """Return a list of binaries generated by the project.""" return wrap_paths_to_binaries( binaries=[("main", BinaryType.EXECUTABLE From b3cad7953a388238aa0264ebdf35e6962a1ff8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Tue, 18 Apr 2023 15:27:17 +0200 Subject: [PATCH 15/26] adapt code to work after pull from vara-dev --- varats/varats/data/reports/feature_blame_report.py | 6 +++--- .../commit_feature_interactions_test_repo.py | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index cbeb5c175..fbd8a4f29 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -33,7 +33,7 @@ def create_feature_tainted_instruction( instruction = str(raw_inst_entry['inst']) location = str(raw_inst_entry['location']) features: tp.List[str] = raw_inst_entry['features'] - crp: tp.Dict[str] = raw_inst_entry['commit'] + crp: tp.Dict[str, any] = raw_inst_entry['commit'] commit: CommitRepoPair = CommitRepoPair( crp['commit'], crp['repository'] ) @@ -46,11 +46,11 @@ def print(self) -> None: print(" -FEATURE COMMIT REGION INSTRUCTION") print(" -INSTRUCTION: " + self.__instruction) print(" -LOCATION: " + self.__location) - print(" -FEATURE REGIONS: ") + print(" -FEATURES: ") for feature_region in self.__features: print(" -" + feature_region) print(" -COMMIT: ") - print(" -HASH: " + self.__commit.commit_hash) + print(" -HASH: " + self.__commit.commit_hash.__str__()) print(" -REPO: " + self.__commit.repository_name) @property diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py index 83c4a531e..0fa7a12a4 100644 --- a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py +++ b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py @@ -8,11 +8,11 @@ from varats.project.project_domain import ProjectDomains from varats.project.project_util import ( ProjectBinaryWrapper, - wrap_paths_to_binaries, + get_local_project_git_path, BinaryType, ) from varats.project.varats_project import VProject -from varats.utils.git_util import ShortCommitHash +from varats.utils.git_util import RevisionBinaryMap, ShortCommitHash class CFI(VProject): # type: ignore @@ -39,10 +39,11 @@ def binaries_for_revision( revision: ShortCommitHash ) -> tp.List[ProjectBinaryWrapper]: """Return a list of binaries generated by the project.""" - return wrap_paths_to_binaries( - binaries=[("main", BinaryType.EXECUTABLE - ), ("helper", BinaryType.EXECUTABLE)] - ) + binary_map = RevisionBinaryMap(get_local_project_git_path(CFI.NAME)) + for test_file in ["main", "helper"]: + binary_map.specify_binary(test_file, BinaryType.EXECUTABLE) + + return binary_map[revision] def run_tests(self) -> None: """This function defines tests and benchmarks for the project.""" From 8a6f71e0576fd4a35774be3f3062a86f5ebdd6d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Mon, 22 May 2023 22:32:27 +0200 Subject: [PATCH 16/26] adapted python fbr to new yaml fbr --- .../data/reports/feature_blame_report.py | 173 +++++------------- 1 file changed, 43 insertions(+), 130 deletions(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index fbd8a4f29..1964801ae 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -11,66 +11,51 @@ from varats.utils.git_util import CommitRepoPair -class FeatureCommitRegionInstruction(): - """An instruction that has one or more feature-commit interaction and its - location in the project.""" +class CommitFeatureInteraction(): + """A CommitFeatureInteraction detailing the specific commit-hash and repo + and feature and the number of instructions this cfi occurs in.""" def __init__( - self, instruction: str, location: str, features: tp.List[str], - commit: CommitRepoPair + self, num_instructions: int, feature: str, commit: CommitRepoPair ) -> None: - self.__instruction = instruction - self.__location = location - self.__features = features + self.__num_instructions = num_instructions + self.__feature = feature self.__commit = commit @staticmethod - def create_feature_tainted_instruction( + def create_commit_feature_interaction( raw_inst_entry: tp.Dict[str, tp.Any] - ) -> 'FeatureCommitRegionInstruction': - """Creates a `FeatureCommitRegionInstruction` entry from the - corresponding yaml document section.""" - instruction = str(raw_inst_entry['inst']) - location = str(raw_inst_entry['location']) - features: tp.List[str] = raw_inst_entry['features'] - crp: tp.Dict[str, any] = raw_inst_entry['commit'] + ) -> 'CommitFeatureInteraction': + """Creates a `CommitFeatureInteraction` entry from the corresponding + yaml document section.""" + num_instructions = int(raw_inst_entry['num-instructions']) + feature: str = str(raw_inst_entry['feature']) commit: CommitRepoPair = CommitRepoPair( - crp['commit'], crp['repository'] - ) - return FeatureCommitRegionInstruction( - instruction, location, features, commit + raw_inst_entry['commit-hash'], raw_inst_entry['commit-repo'] ) + return CommitFeatureInteraction(num_instructions, feature, commit) def print(self) -> None: - """'FeatureCommitRegionInstruction' prints itself.""" - print(" -FEATURE COMMIT REGION INSTRUCTION") - print(" -INSTRUCTION: " + self.__instruction) - print(" -LOCATION: " + self.__location) - print(" -FEATURES: ") - for feature_region in self.__features: - print(" -" + feature_region) - print(" -COMMIT: ") + """'CommitFeatureInteraction' prints itself.""" + print(" -COMMIT FEATURE INTERACTION") + print(" -NUM OF INSTRUCTIONS: " + str(self.__num_instructions)) + print(" -FEATURE: " + self.__feature) print(" -HASH: " + self.__commit.commit_hash.__str__()) print(" -REPO: " + self.__commit.repository_name) @property - def instruction(self) -> str: - """instruction containg commit and feature regions.""" - return self.__instruction - - @property - def location(self) -> str: - """Location of instruction in the project.""" - return self.__location + def num_instructions(self) -> int: + """number of instructions the specified cfi occurs in.""" + return self.__num_instructions @property - def features(self) -> tp.List[str]: - """List of feature regions of this instruction.""" - return self.__features + def feature(self) -> str: + """The feature of this cfi.""" + return self.__feature @property - def commit_region(self) -> CommitRepoPair: - """commit region of this instruction.""" + def commit(self) -> CommitRepoPair: + """commit of this cfi.""" return self.__commit def is_terminator(self) -> bool: @@ -78,64 +63,6 @@ def is_terminator(self) -> bool: return br_regex.search(self.__instruction) is not None -class FeatureBlameResultFunctionEntry(): - """Collection of all feature commit region instructions for a specific - function.""" - - def __init__( - self, name: str, demangled_name: str, - feature_commit_region_insts: tp.List[FeatureCommitRegionInstruction] - ) -> None: - self.__name = name - self.__demangled_name = demangled_name - self.__feature_commit_region_insts = feature_commit_region_insts - - @staticmethod - def create_feature_blame_result_function_entry( - name: str, raw_function_entry: tp.Dict[str, tp.Any] - ) -> 'FeatureBlameResultFunctionEntry': - """Creates a `FeatureBlameResultFunctionEntry` from the corresponding - yaml document section.""" - demangled_name = str(raw_function_entry['demangled-name']) - inst_list: tp.List[FeatureCommitRegionInstruction] = [] - for raw_inst_entry in raw_function_entry[ - 'commit-feature-interaction-related-insts']: - inst_list.append( - FeatureCommitRegionInstruction. - create_feature_tainted_instruction(raw_inst_entry) - ) - return FeatureBlameResultFunctionEntry(name, demangled_name, inst_list) - - def print(self) -> None: - """'FeatureBlameResultFunctionEntry' prints itself.""" - print(" -FEATURE BLAME RESULT FUNCTION ENTRY") - print(" -FUNCTION: " + self.demangled_name) - for feature_commit_region_inst in self.__feature_commit_region_insts: - feature_commit_region_inst.print() - - @property - def name(self) -> str: - """ - Name of the function. - - The name is mangled for C++ code, either with the itanium or windows - mangling schema. - """ - return self.__name - - @property - def demangled_name(self) -> str: - """Demangled name of the function.""" - return self.__demangled_name - - @property - def feature_commit_region_insts( - self - ) -> tp.List[FeatureCommitRegionInstruction]: - """List of found feature commit region instructions.""" - return self.__feature_commit_region_insts - - class FeatureBlameReportMetaData(): """Provides extra meta data about llvm::Module, which was analyzed to generate this ``FeatureBlameReport``.""" @@ -194,25 +121,26 @@ def __init__(self, path: Path) -> None: self.__meta_data = FeatureBlameReportMetaData \ .create_feature_analysis_report_meta_data(next(documents)) - self.__function_entries: tp.Dict[ - str, FeatureBlameResultFunctionEntry] = {} + self.__commit_feature_interactions: tp.Dict[ + str, CommitFeatureInteraction] = {} raw_feature_blame_report = next(documents) - for raw_func_entry in raw_feature_blame_report['result-map']: - new_function_entry = ( - FeatureBlameResultFunctionEntry. - create_feature_blame_result_function_entry( - raw_func_entry, - raw_feature_blame_report['result-map'][raw_func_entry] + counter: int = 1 + for cfi in raw_feature_blame_report['commit-feature-interactions']: + key: str = "cfi_" + str(counter) + new_cfi = ( + CommitFeatureInteraction.create_commit_feature_interaction( + raw_feature_blame_report['commit-feature-interactions'] + [key] ) ) - self.__function_entries[new_function_entry.name - ] = new_function_entry + self.__commit_feature_interactions[key] = new_cfi + counter = counter + 1 def print(self) -> None: """'FeatureBlameReport' prints itself.""" print("FEATURE BLAME REPORT") - for feature_blame_result_func_entr in self.__function_entries.values(): - feature_blame_result_func_entr.print() + for cfi in self.__commit_feature_interactions.values(): + cfi.print() @property def meta_data(self) -> FeatureBlameReportMetaData: @@ -221,23 +149,8 @@ def meta_data(self) -> FeatureBlameReportMetaData: return self.__meta_data @property - def function_entries( + def commit_feature_interactions( self - ) -> tp.ValuesView[FeatureBlameResultFunctionEntry]: - """Iterate over all function entries.""" - return self.__function_entries.values() - - def get_feature_analysis_result_function_entry( - self, mangled_function_name: str - ) -> FeatureBlameResultFunctionEntry: - """ - Get the result entry for a specific function. - - Args: - mangled_function_name: mangled name of the function to look up - """ - return self.__function_entries[mangled_function_name] - - # def get_feature_locations_dict(self) -> tp.Dict[str, tp.Set[str]]: - # """Returns a dictionary that maps a feature name to a list of all - # locations of tainted br and switch instructions.""" + ) -> tp.ValuesView[CommitFeatureInteraction]: + """Iterate over all cfis.""" + return self.__commit_feature_interactions.values() From 2c56155ef0d97c44232a31d45b6c7240894f6396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Sun, 25 Jun 2023 13:23:06 +0200 Subject: [PATCH 17/26] separated feature blame report Structural- and DataflowFeatureBlameReport can be generated separatedly now --- .../data/reports/feature_blame_report.py | 204 ++++++++++++------ .../vara/feature_blame_report_experiment.py | 160 ++++++++++++-- .../commit_feature_interactions_test_repo.py | 2 +- 3 files changed, 280 insertions(+), 86 deletions(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index 1964801ae..25346c6bc 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -1,4 +1,4 @@ -"""Module for FeatureBlameReport.""" +"""Module for StructuralFeatureBlameReport and DataflowFeatureBlameReport.""" import re import typing as tp @@ -7,13 +7,18 @@ import yaml from varats.base.version_header import VersionHeader +from varats.data.reports.blame_report import BlameReportMetaData +from varats.data.reports.feature_analysis_report import ( + FeatureAnalysisReportMetaData, +) from varats.report.report import BaseReport from varats.utils.git_util import CommitRepoPair -class CommitFeatureInteraction(): - """A CommitFeatureInteraction detailing the specific commit-hash and repo - and feature and the number of instructions this cfi occurs in.""" +class StructuralCommitFeatureInteraction(): + """A StructuralCommitFeatureInteraction detailing the specific commit-hash + and repo and feature and the number of instructions this structural cfi + occurs in.""" def __init__( self, num_instructions: int, feature: str, commit: CommitRepoPair @@ -25,23 +30,26 @@ def __init__( @staticmethod def create_commit_feature_interaction( raw_inst_entry: tp.Dict[str, tp.Any] - ) -> 'CommitFeatureInteraction': - """Creates a `CommitFeatureInteraction` entry from the corresponding - yaml document section.""" + ) -> 'StructuralCommitFeatureInteraction': + """Creates a `StructuralCommitFeatureInteraction` entry from the + corresponding yaml document section.""" num_instructions = int(raw_inst_entry['num-instructions']) feature: str = str(raw_inst_entry['feature']) commit: CommitRepoPair = CommitRepoPair( - raw_inst_entry['commit-hash'], raw_inst_entry['commit-repo'] + (raw_inst_entry['commit-repo-pair'])['commit'], + (raw_inst_entry['commit-repo-pair'])['repository'] + ) + return StructuralCommitFeatureInteraction( + num_instructions, feature, commit ) - return CommitFeatureInteraction(num_instructions, feature, commit) def print(self) -> None: """'CommitFeatureInteraction' prints itself.""" print(" -COMMIT FEATURE INTERACTION") print(" -NUM OF INSTRUCTIONS: " + str(self.__num_instructions)) print(" -FEATURE: " + self.__feature) - print(" -HASH: " + self.__commit.commit_hash.__str__()) - print(" -REPO: " + self.__commit.repository_name) + print(" -HASH: " + self.__commit.commit_hash.__str__()) + print(" -REPO: " + self.__commit.repository_name) @property def num_instructions(self) -> int: @@ -60,54 +68,118 @@ def commit(self) -> CommitRepoPair: def is_terminator(self) -> bool: br_regex = re.compile(r'(br( i1 | label ))|(switch i\d{1,} )') - return br_regex.search(self.__instruction) is not None + return br_regex.search(self.__num_instructions) is not None -class FeatureBlameReportMetaData(): - """Provides extra meta data about llvm::Module, which was analyzed to - generate this ``FeatureBlameReport``.""" +class StructuralFeatureBlameReportMetaData(FeatureAnalysisReportMetaData): + pass - def __init__( - self, num_functions: int, num_instructions: int, - num_br_switch_insts: int - ) -> None: - self.__number_of_functions_in_module = num_functions - self.__number_of_instructions_in_module = num_instructions - self.__number_of_branch_and_switch_ints_in_module = num_br_switch_insts - @property - def num_functions(self) -> int: - """Number of functions in the analyzed llvm::Module.""" - return self.__number_of_functions_in_module +class StructuralFeatureBlameReport( + BaseReport, shorthand="SFBR", file_type="yaml" +): + """Data class that gives access to a loaded structural feature blame + report.""" + + def __init__(self, path: Path) -> None: + super().__init__(path) + + with open(path, 'r') as stream: + documents = yaml.load_all(stream, Loader=yaml.CLoader) + version_header = VersionHeader(next(documents)) + version_header.raise_if_not_type("StructuralFeatureBlameReport") + version_header.raise_if_version_is_less_than(1) + + self.__meta_data = StructuralFeatureBlameReportMetaData \ + .create_feature_analysis_report_meta_data(next(documents)) + + self.__commit_feature_interactions: tp.List[ + StructuralCommitFeatureInteraction] = [] + raw_feature_blame_report = next(documents) + for cfi in raw_feature_blame_report[ + 'structural-commit-feature-interactions']: + new_cfi = ( + StructuralCommitFeatureInteraction. + create_commit_feature_interaction(cfi) + ) + self.__commit_feature_interactions.append(new_cfi) + + def print(self) -> None: + """'FeatureBlameReport' prints itself.""" + print("STRUCTURAL FEATURE BLAME REPORT") + for cfi in self.__commit_feature_interactions: + cfi.print() @property - def num_instructions(self) -> int: - """Number of instructions processed in the analyzed llvm::Module.""" - return self.__number_of_instructions_in_module + def meta_data(self) -> StructuralFeatureBlameReportMetaData: + """Access the meta data that was gathered with the + ``StructuralFeatureBlameReport``.""" + return self.__meta_data @property - def num_br_switch_insts(self) -> int: - """Number of branch and switch instructions processed in the analyzed - llvm::Module.""" - return self.__number_of_branch_and_switch_ints_in_module + def commit_feature_interactions( + self + ) -> tp.ValuesView[StructuralCommitFeatureInteraction]: + """Iterate over all cfis.""" + return self.__commit_feature_interactions + + +##### DATAFLOW ##### + + +class DataflowCommitFeatureInteraction(): + """A DataflowCommitFeatureInteraction detailing the specific commit-hash and + repo and feature this dataflow-based cfi occurs in.""" + + def __init__(self, feature: str, commits: tp.List[CommitRepoPair]) -> None: + self.__feature = feature + self.__commits = commits @staticmethod - def create_feature_analysis_report_meta_data( - raw_document: tp.Dict[str, tp.Any] - ) -> 'FeatureBlameReportMetaData': - """Creates `FeatureBlameReportMetaData` from the corresponding yaml - document.""" - num_functions = int(raw_document['funcs-in-module']) - num_instructions = int(raw_document['insts-in-module']) - num_br_switch_insts = int(raw_document['br-switch-insts-in-module']) - - return FeatureBlameReportMetaData( - num_functions, num_instructions, num_br_switch_insts - ) + def create_commit_feature_interaction( + raw_inst_entry: tp.Dict[str, tp.Any] + ) -> 'DataflowCommitFeatureInteraction': + """Creates a `DataflowCommitFeatureInteraction` entry from the + corresponding yaml document section.""" + feature: str = str(raw_inst_entry['feature']) + crps: tp.List[CommitRepoPair] = [] + for crp in raw_inst_entry['commit-repo-pairs']: + crps.append(CommitRepoPair(crp['commit'], crp['repository'])) + return DataflowCommitFeatureInteraction(feature, crps) + def print(self) -> None: + """'CommitFeatureInteraction' prints itself.""" + print(" -COMMIT FEATURE INTERACTION") + print(" -FEATURE: " + self.__feature) + print(" -COMMITS: ") + for commit in self.__commits: + print(" -COMMIT: " + commit.commit_hash) + print(" -REPO: " + commit.repository_name) + + @property + def feature(self) -> str: + """The feature of this cfi.""" + return self.__feature + + @property + def commit(self) -> tp.List[CommitRepoPair]: + """commit of this cfi.""" + return self.__commits + + def is_terminator(self) -> bool: + br_regex = re.compile(r'(br( i1 | label ))|(switch i\d{1,} )') + return br_regex.search(self.__commits) is not None + + +class DataflowFeatureBlameReportMetaData(BlameReportMetaData): + pass -class FeatureBlameReport(BaseReport, shorthand="FBR", file_type="yaml"): - """Data class that gives access to a loaded feature blame report.""" + +class DataflowFeatureBlameReport( + BaseReport, shorthand="DFBR", file_type="yaml" +): + """Data class that gives access to a loaded dataflow feature blame + report.""" def __init__(self, path: Path) -> None: super().__init__(path) @@ -115,42 +187,38 @@ def __init__(self, path: Path) -> None: with open(path, 'r') as stream: documents = yaml.load_all(stream, Loader=yaml.CLoader) version_header = VersionHeader(next(documents)) - version_header.raise_if_not_type("FeatureBlameReport") + version_header.raise_if_not_type("DataflowFeatureBlameReport") version_header.raise_if_version_is_less_than(1) - self.__meta_data = FeatureBlameReportMetaData \ - .create_feature_analysis_report_meta_data(next(documents)) + self.__meta_data = DataflowFeatureBlameReportMetaData \ + .create_blame_report_meta_data(next(documents)) - self.__commit_feature_interactions: tp.Dict[ - str, CommitFeatureInteraction] = {} + self.__commit_feature_interactions: tp.List[ + DataflowCommitFeatureInteraction] = [] raw_feature_blame_report = next(documents) - counter: int = 1 - for cfi in raw_feature_blame_report['commit-feature-interactions']: - key: str = "cfi_" + str(counter) + for cfi in raw_feature_blame_report[ + 'dataflow-commit-feature-interactions']: new_cfi = ( - CommitFeatureInteraction.create_commit_feature_interaction( - raw_feature_blame_report['commit-feature-interactions'] - [key] - ) + DataflowCommitFeatureInteraction. + create_commit_feature_interaction(cfi) ) - self.__commit_feature_interactions[key] = new_cfi - counter = counter + 1 + self.__commit_feature_interactions.append(new_cfi) def print(self) -> None: - """'FeatureBlameReport' prints itself.""" - print("FEATURE BLAME REPORT") - for cfi in self.__commit_feature_interactions.values(): + """'DataflowFeatureBlameReport' prints itself.""" + print("DATAFLOW FEATURE BLAME REPORT") + for cfi in self.__commit_feature_interactions: cfi.print() @property - def meta_data(self) -> FeatureBlameReportMetaData: + def meta_data(self) -> DataflowFeatureBlameReportMetaData: """Access the meta data that was gathered with the - ``FeatureBlameReport``.""" + ``DataflowFeatureBlameReport``.""" return self.__meta_data @property def commit_feature_interactions( self - ) -> tp.ValuesView[CommitFeatureInteraction]: + ) -> tp.ValuesView[DataflowCommitFeatureInteraction]: """Iterate over all cfis.""" - return self.__commit_feature_interactions.values() + return self.__commit_feature_interactions diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index 40337f55f..5020155eb 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -13,7 +13,12 @@ from benchbuild.utils.requirements import Requirement, SlurmMem import varats.experiments.vara.feature_blame_experiment as FBE -from varats.data.reports.feature_blame_report import FeatureBlameReport as FBR +from varats.data.reports.feature_blame_report import ( + StructuralFeatureBlameReport as SFBR, +) +from varats.data.reports.feature_blame_report import ( + DataflowFeatureBlameReport as DFBR, +) from varats.experiment.experiment_util import ( exec_func_with_pe_error_handler, VersionExperiment, @@ -28,11 +33,130 @@ from varats.report.report import ReportSpecification -class FeatureBlameReportGeneration(actions.ProjectStep): # type: ignore - """Analyse a project with VaRA and generate a FeatureBlameReport.""" +class StructuralFeatureBlameReportGeneration( + actions.ProjectStep +): # type: ignore + """Analyse a project with VaRA and generate a + StructuralFeatureBlameReport.""" + + NAME = "StructuralFeatureBlameReportGeneration" + DESCRIPTION = "Analyses the bitcode with -vara-SFBR of VaRA." + + project: VProject + + def __init__( + self, + project: Project, + experiment_handle: ExperimentHandle, + ): + super().__init__(project=project) + self.__experiment_handle = experiment_handle + + def __call__(self) -> actions.StepResult: + return self.analyze() + + def analyze(self) -> actions.StepResult: + """ + This step performs the actual analysis with the correct command line + flags. Flags used: + + * -vara-SFBR: to run a structural commit feature interaction report + * -yaml-report-outfile=: specify the path to store the results + """ + for binary in self.project.binaries: + # Add to the user-defined path for saving the results of the + # analysis also the name and the unique id of the project of every + # run. + result_file = create_new_success_result_filepath( + self.__experiment_handle, SFBR, self.project, binary + ) + + opt_params = [ + "--enable-new-pm=0", "-vara-PTFDD", "-vara-BD", "-vara-SFBR", + "-vara-init-commits", "-vara-use-phasar", + f"-vara-report-outfile={result_file}", + get_cached_bc_file_path( + self.project, binary, [ + BCFileExtensions.NO_OPT, BCFileExtensions.TBAA, + BCFileExtensions.BLAME, BCFileExtensions.FEATURE + ] + ) + ] + + run_cmd = opt[opt_params] + + run_cmd = wrap_unlimit_stack_size(run_cmd) + + exec_func_with_pe_error_handler( + run_cmd, + create_default_analysis_failure_handler( + self.__experiment_handle, self.project, SFBR + ) + ) + + test_fbr = SFBR(path=result_file.full_path()) + + test_fbr.print() + + return actions.StepResult.OK + + +class StructuralFeatureBlameReportExperiment( + VersionExperiment, shorthand="SFBRE" +): + """Generates a structural feature blame report of the project(s) specified + in the call.""" + + NAME = "GenerateStructuralFeatureBlameReport" + + REPORT_SPEC = ReportSpecification(SFBR) + REQUIREMENTS: tp.List[Requirement] = [SlurmMem("250G")] + + def actions_for_project( + self, project: VProject + ) -> tp.MutableSequence[actions.Step]: + """ + Returns the specified steps to run the project(s) specified in the call + in a fixed order. + + Args: + project: to analyze + """ + # Try, to build the project without optimizations to get more precise + # blame annotations. Note: this does not guarantee that a project is + # build without optimizations because the used build tool/script can + # still add optimizations flags after the experiment specified cflags. + project.cflags += ["-O1", "-Xclang", "-disable-llvm-optzns", "-g0"] + bc_file_extensions = [ + BCFileExtensions.NO_OPT, BCFileExtensions.TBAA, + BCFileExtensions.BLAME, BCFileExtensions.FEATURE + ] + + FBE.setup_basic_feature_blame_experiment(self, project, SFBR) + + analysis_actions = FBE.generate_basic_feature_blame_experiment_actions( + project, + bc_file_extensions, + extraction_error_handler=create_default_compiler_error_handler( + self.get_handle(), project, self.REPORT_SPEC.main_report + ) + ) + analysis_actions.append( + StructuralFeatureBlameReportGeneration(project, self.get_handle()) + ) + analysis_actions.append(actions.Clean(project)) + + return analysis_actions + + +#### DATAFLOW #### + + +class DataflowFeatureBlameReportGeneration(actions.ProjectStep): # type: ignore + """Analyse a project with VaRA and generate a DataflowFeatureBlameReport.""" - NAME = "FeatureBlameReportGeneration" - DESCRIPTION = "Analyses the bitcode with -vara-FBR of VaRA." + NAME = "DataflowFeatureBlameReportGeneration" + DESCRIPTION = "Analyses the bitcode with -vara-DFBR of VaRA." project: VProject @@ -52,7 +176,7 @@ def analyze(self) -> actions.StepResult: This step performs the actual analysis with the correct command line flags. Flags used: - * -vara-FBR: to run a commit feature interaction report + * -vara-DFBR: to run a commit feature interaction through dataflow report * -yaml-report-outfile=: specify the path to store the results """ for binary in self.project.binaries: @@ -60,11 +184,11 @@ def analyze(self) -> actions.StepResult: # analysis also the name and the unique id of the project of every # run. result_file = create_new_success_result_filepath( - self.__experiment_handle, FBR, self.project, binary + self.__experiment_handle, DFBR, self.project, binary ) opt_params = [ - "--enable-new-pm=0", "-vara-PTFDD", "-vara-BD", "-vara-FBR", + "--enable-new-pm=0", "-vara-PTFDD", "-vara-BD", "-vara-DFBR", "-vara-init-commits", "-vara-use-phasar", f"-vara-report-outfile={result_file}", get_cached_bc_file_path( @@ -82,24 +206,26 @@ def analyze(self) -> actions.StepResult: exec_func_with_pe_error_handler( run_cmd, create_default_analysis_failure_handler( - self.__experiment_handle, self.project, FBR + self.__experiment_handle, self.project, DFBR ) ) - test_fbr = FBR(path=result_file.full_path()) + test_fbr = DFBR(path=result_file.full_path()) test_fbr.print() return actions.StepResult.OK -class FeatureBlameReportExperiment(VersionExperiment, shorthand="FBRE"): - """Generates a feature blame report of the project(s) specified in the - call.""" +class DataflowFeatureBlameReportExperiment( + VersionExperiment, shorthand="SFBRE" +): + """Generates a dataflow feature blame report of the project(s) specified in + the call.""" - NAME = "GenerateFeatureBlameReport" + NAME = "GenerateDataflowFeatureBlameReport" - REPORT_SPEC = ReportSpecification(FBR) + REPORT_SPEC = ReportSpecification(DFBR) REQUIREMENTS: tp.List[Requirement] = [SlurmMem("250G")] def actions_for_project( @@ -122,7 +248,7 @@ def actions_for_project( BCFileExtensions.BLAME, BCFileExtensions.FEATURE ] - FBE.setup_basic_feature_blame_experiment(self, project, FBR) + FBE.setup_basic_feature_blame_experiment(self, project, DFBR) analysis_actions = FBE.generate_basic_feature_blame_experiment_actions( project, @@ -132,7 +258,7 @@ def actions_for_project( ) ) analysis_actions.append( - FeatureBlameReportGeneration(project, self.get_handle()) + DataflowFeatureBlameReportGeneration(project, self.get_handle()) ) analysis_actions.append(actions.Clean(project)) diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py index 0fa7a12a4..d46d6353e 100644 --- a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py +++ b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py @@ -32,7 +32,7 @@ class CFI(VProject): # type: ignore ) ] - test_files = ["main.cpp", "helper.cpp"] + test_files = ["main.cpp", "helper.cpp", "dataflow.cpp", "more_dataflow.cpp"] @staticmethod def binaries_for_revision( From 5e221e1d6046c54a5cc581275814739bbaee16c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Mon, 10 Jul 2023 13:05:31 +0200 Subject: [PATCH 18/26] added detection of feature model --- .../vara/feature_blame_report_experiment.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index 5020155eb..64e238ea2 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -30,6 +30,10 @@ ) from varats.experiment.wllvm import get_cached_bc_file_path, BCFileExtensions from varats.project.varats_project import VProject +from varats.provider.feature.feature_model_provider import ( + FeatureModelNotFound, + FeatureModelProvider, +) from varats.report.report import ReportSpecification @@ -122,11 +126,23 @@ def actions_for_project( Args: project: to analyze """ + # FeatureModelProvider + fm_provider = FeatureModelProvider.create_provider_for_project(project) + if fm_provider is None: + raise FeatureModelNotFound(project, None) + + fm_path = fm_provider.get_feature_model_path(project) + + if fm_path is None or not fm_path.exists(): + raise FeatureModelNotFound(project, fm_path) # Try, to build the project without optimizations to get more precise # blame annotations. Note: this does not guarantee that a project is # build without optimizations because the used build tool/script can # still add optimizations flags after the experiment specified cflags. - project.cflags += ["-O1", "-Xclang", "-disable-llvm-optzns", "-g0"] + project.cflags += [ + f"-fvara-fm-path={fm_path.absolute()}", "-O1", "-Xclang", + "-disable-llvm-optzns", "-g0" + ] bc_file_extensions = [ BCFileExtensions.NO_OPT, BCFileExtensions.TBAA, BCFileExtensions.BLAME, BCFileExtensions.FEATURE From dafb1ba9963f0d9e1b81ade818ed69e3323961ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Thu, 7 Sep 2023 13:58:44 +0200 Subject: [PATCH 19/26] refactoring --- .../data/reports/feature_blame_report.py | 55 +++----------- .../experiments/vara/blame_experiment.py | 73 ------------------- .../vara/blame_report_experiment.py | 2 +- .../vara/blame_verifier_experiment.py | 2 +- .../vara/feature_blame_report_experiment.py | 15 +--- ...lame_experiment.py => vara_experiments.py} | 58 ++++++++++++++- .../commit_feature_interactions_test_repo.py | 57 --------------- 7 files changed, 73 insertions(+), 189 deletions(-) delete mode 100644 varats/varats/experiments/vara/blame_experiment.py rename varats/varats/experiments/vara/{feature_blame_experiment.py => vara_experiments.py} (53%) delete mode 100644 varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index 25346c6bc..5517c37d6 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -43,14 +43,6 @@ def create_commit_feature_interaction( num_instructions, feature, commit ) - def print(self) -> None: - """'CommitFeatureInteraction' prints itself.""" - print(" -COMMIT FEATURE INTERACTION") - print(" -NUM OF INSTRUCTIONS: " + str(self.__num_instructions)) - print(" -FEATURE: " + self.__feature) - print(" -HASH: " + self.__commit.commit_hash.__str__()) - print(" -REPO: " + self.__commit.repository_name) - @property def num_instructions(self) -> int: """number of instructions the specified cfi occurs in.""" @@ -68,10 +60,10 @@ def commit(self) -> CommitRepoPair: def is_terminator(self) -> bool: br_regex = re.compile(r'(br( i1 | label ))|(switch i\d{1,} )') - return br_regex.search(self.__num_instructions) is not None + return br_regex.search(self.__feature) is not None -class StructuralFeatureBlameReportMetaData(FeatureAnalysisReportMetaData): +class FeatureBlameReportMetaData(FeatureAnalysisReportMetaData): pass @@ -90,7 +82,7 @@ def __init__(self, path: Path) -> None: version_header.raise_if_not_type("StructuralFeatureBlameReport") version_header.raise_if_version_is_less_than(1) - self.__meta_data = StructuralFeatureBlameReportMetaData \ + self.__meta_data = FeatureBlameReportMetaData \ .create_feature_analysis_report_meta_data(next(documents)) self.__commit_feature_interactions: tp.List[ @@ -104,14 +96,8 @@ def __init__(self, path: Path) -> None: ) self.__commit_feature_interactions.append(new_cfi) - def print(self) -> None: - """'FeatureBlameReport' prints itself.""" - print("STRUCTURAL FEATURE BLAME REPORT") - for cfi in self.__commit_feature_interactions: - cfi.print() - @property - def meta_data(self) -> StructuralFeatureBlameReportMetaData: + def meta_data(self) -> FeatureBlameReportMetaData: """Access the meta data that was gathered with the ``StructuralFeatureBlameReport``.""" return self.__meta_data @@ -119,8 +105,8 @@ def meta_data(self) -> StructuralFeatureBlameReportMetaData: @property def commit_feature_interactions( self - ) -> tp.ValuesView[StructuralCommitFeatureInteraction]: - """Iterate over all cfis.""" + ) -> tp.List[StructuralCommitFeatureInteraction]: + """Return all structural cfis.""" return self.__commit_feature_interactions @@ -147,15 +133,6 @@ def create_commit_feature_interaction( crps.append(CommitRepoPair(crp['commit'], crp['repository'])) return DataflowCommitFeatureInteraction(feature, crps) - def print(self) -> None: - """'CommitFeatureInteraction' prints itself.""" - print(" -COMMIT FEATURE INTERACTION") - print(" -FEATURE: " + self.__feature) - print(" -COMMITS: ") - for commit in self.__commits: - print(" -COMMIT: " + commit.commit_hash) - print(" -REPO: " + commit.repository_name) - @property def feature(self) -> str: """The feature of this cfi.""" @@ -168,11 +145,7 @@ def commit(self) -> tp.List[CommitRepoPair]: def is_terminator(self) -> bool: br_regex = re.compile(r'(br( i1 | label ))|(switch i\d{1,} )') - return br_regex.search(self.__commits) is not None - - -class DataflowFeatureBlameReportMetaData(BlameReportMetaData): - pass + return br_regex.search(self.__feature) is not None class DataflowFeatureBlameReport( @@ -190,7 +163,7 @@ def __init__(self, path: Path) -> None: version_header.raise_if_not_type("DataflowFeatureBlameReport") version_header.raise_if_version_is_less_than(1) - self.__meta_data = DataflowFeatureBlameReportMetaData \ + self.__meta_data = FeatureBlameReportMetaData \ .create_blame_report_meta_data(next(documents)) self.__commit_feature_interactions: tp.List[ @@ -204,14 +177,8 @@ def __init__(self, path: Path) -> None: ) self.__commit_feature_interactions.append(new_cfi) - def print(self) -> None: - """'DataflowFeatureBlameReport' prints itself.""" - print("DATAFLOW FEATURE BLAME REPORT") - for cfi in self.__commit_feature_interactions: - cfi.print() - @property - def meta_data(self) -> DataflowFeatureBlameReportMetaData: + def meta_data(self) -> FeatureBlameReportMetaData: """Access the meta data that was gathered with the ``DataflowFeatureBlameReport``.""" return self.__meta_data @@ -219,6 +186,6 @@ def meta_data(self) -> DataflowFeatureBlameReportMetaData: @property def commit_feature_interactions( self - ) -> tp.ValuesView[DataflowCommitFeatureInteraction]: - """Iterate over all cfis.""" + ) -> tp.List[DataflowCommitFeatureInteraction]: + """Return all dataflow-based cfis.""" return self.__commit_feature_interactions diff --git a/varats/varats/experiments/vara/blame_experiment.py b/varats/varats/experiments/vara/blame_experiment.py deleted file mode 100644 index f2f1617fb..000000000 --- a/varats/varats/experiments/vara/blame_experiment.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Implements the base blame experiment, making it easier to create different -blame experiments that have a similar experiment setup.""" - -import typing as tp - -from benchbuild import Project -from benchbuild.extensions import compiler, run, time -from benchbuild.utils import actions - -from varats.experiment.experiment_util import ( - VersionExperiment, - get_default_compile_error_wrapped, - PEErrorHandler, -) -from varats.experiment.wllvm import ( - RunWLLVM, - BCFileExtensions, - get_bc_cache_actions, -) -from varats.report.report import BaseReport - - -def setup_basic_blame_experiment( - experiment: VersionExperiment, project: Project, - report_type: tp.Type[BaseReport] -) -> None: - """ - Setup the project for a blame experiment. - - - run time extensions - - compile time extensions - - prepare compiler - - configure C/CXX flags - """ - # Add the required runtime extensions to the project(s). - project.runtime_extension = run.RuntimeExtension(project, experiment) \ - << time.RunWithTime() - - # Add the required compiler extensions to the project(s). - project.compiler_extension = compiler.RunCompiler(project, experiment) \ - << RunWLLVM() \ - << run.WithTimeout() - - # Add own error handler to compile step. - project.compile = get_default_compile_error_wrapped( - experiment.get_handle(), project, report_type - ) - - # This c-flag is provided by VaRA and it suggests to use the git-blame - # annotation. - project.cflags += ["-fvara-GB"] - - -def generate_basic_blame_experiment_actions( - project: Project, - bc_file_extensions: tp.Optional[tp.List[BCFileExtensions]] = None, - extraction_error_handler: tp.Optional[PEErrorHandler] = None -) -> tp.List[actions.Step]: - """ - Generate the basic actions for a blame experiment. - - - handle caching of BC files - - compile project, if needed - - Args: - project: reference to the BB project - bc_file_extensions: list of bitcode file extensions (e.g. opt, no opt) - extraction_error_handler: handler to manage errors during the - extraction process - """ - return get_bc_cache_actions( - project, bc_file_extensions, extraction_error_handler - ) diff --git a/varats/varats/experiments/vara/blame_report_experiment.py b/varats/varats/experiments/vara/blame_report_experiment.py index 747e8d313..cf5ea5b20 100644 --- a/varats/varats/experiments/vara/blame_report_experiment.py +++ b/varats/varats/experiments/vara/blame_report_experiment.py @@ -12,7 +12,7 @@ from benchbuild.utils.cmd import opt from benchbuild.utils.requirements import Requirement, SlurmMem -import varats.experiments.vara.blame_experiment as BE +import varats.experiments.vara.vara_experiments as BE from varats.data.reports.blame_report import BlameReport as BR from varats.data.reports.blame_report import BlameTaintScope from varats.experiment.experiment_util import ( diff --git a/varats/varats/experiments/vara/blame_verifier_experiment.py b/varats/varats/experiments/vara/blame_verifier_experiment.py index cfea5543a..7d2419e9b 100644 --- a/varats/varats/experiments/vara/blame_verifier_experiment.py +++ b/varats/varats/experiments/vara/blame_verifier_experiment.py @@ -12,7 +12,7 @@ from benchbuild.utils import actions from benchbuild.utils.cmd import opt, timeout -import varats.experiments.vara.blame_experiment as BE +import varats.experiments.vara.vara_experiments as BE from varats.data.reports.blame_verifier_report import ( BlameVerifierReportOpt as BVR_Opt, ) diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index 64e238ea2..ec28f2ef4 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -12,7 +12,7 @@ from benchbuild.utils.cmd import opt from benchbuild.utils.requirements import Requirement, SlurmMem -import varats.experiments.vara.feature_blame_experiment as FBE +import varats.experiments.vara.vara_experiments as FBE from varats.data.reports.feature_blame_report import ( StructuralFeatureBlameReport as SFBR, ) @@ -98,10 +98,6 @@ def analyze(self) -> actions.StepResult: ) ) - test_fbr = SFBR(path=result_file.full_path()) - - test_fbr.print() - return actions.StepResult.OK @@ -192,7 +188,8 @@ def analyze(self) -> actions.StepResult: This step performs the actual analysis with the correct command line flags. Flags used: - * -vara-DFBR: to run a commit feature interaction through dataflow report + * -vara-DFBR: to run a dataflow-based + commit feature interaction report * -yaml-report-outfile=: specify the path to store the results """ for binary in self.project.binaries: @@ -226,15 +223,11 @@ def analyze(self) -> actions.StepResult: ) ) - test_fbr = DFBR(path=result_file.full_path()) - - test_fbr.print() - return actions.StepResult.OK class DataflowFeatureBlameReportExperiment( - VersionExperiment, shorthand="SFBRE" + VersionExperiment, shorthand="DFBRE" ): """Generates a dataflow feature blame report of the project(s) specified in the call.""" diff --git a/varats/varats/experiments/vara/feature_blame_experiment.py b/varats/varats/experiments/vara/vara_experiments.py similarity index 53% rename from varats/varats/experiments/vara/feature_blame_experiment.py rename to varats/varats/experiments/vara/vara_experiments.py index cedd1be6f..df78a6572 100644 --- a/varats/varats/experiments/vara/feature_blame_experiment.py +++ b/varats/varats/experiments/vara/vara_experiments.py @@ -1,5 +1,6 @@ -"""Implements the base feature blame experiment, making it easier to create -different feature-blame experiments that have a similar experiment setup.""" +"""Implements the base blame and feature blame experiment, making it easier to +create different blame and feature blame experiments that have a similar +experiment setup.""" import typing as tp @@ -20,6 +21,37 @@ from varats.report.report import BaseReport +def setup_basic_blame_experiment( + experiment: VersionExperiment, project: Project, + report_type: tp.Type[BaseReport] +) -> None: + """ + Setup the project for a blame experiment. + + - run time extensions + - compile time extensions + - prepare compiler + - configure C/CXX flags + """ + # Add the required runtime extensions to the project(s). + project.runtime_extension = run.RuntimeExtension(project, experiment) \ + << time.RunWithTime() + + # Add the required compiler extensions to the project(s). + project.compiler_extension = compiler.RunCompiler(project, experiment) \ + << RunWLLVM() \ + << run.WithTimeout() + + # Add own error handler to compile step. + project.compile = get_default_compile_error_wrapped( + experiment.get_handle(), project, report_type + ) + + # This c-flag is provided by VaRA and it suggests to use the git-blame + # annotation. + project.cflags += ["-fvara-GB"] + + def setup_basic_feature_blame_experiment( experiment: VersionExperiment, project: Project, report_type: tp.Type[BaseReport] @@ -51,6 +83,28 @@ def setup_basic_feature_blame_experiment( project.cflags += ["-fvara-GB", "-fvara-feature"] +def generate_basic_blame_experiment_actions( + project: Project, + bc_file_extensions: tp.Optional[tp.List[BCFileExtensions]] = None, + extraction_error_handler: tp.Optional[PEErrorHandler] = None +) -> tp.List[actions.Step]: + """ + Generate the basic actions for a blame experiment. + + - handle caching of BC files + - compile project, if needed + + Args: + project: reference to the BB project + bc_file_extensions: list of bitcode file extensions (e.g. opt, no opt) + extraction_error_handler: handler to manage errors during the + extraction process + """ + return get_bc_cache_actions( + project, bc_file_extensions, extraction_error_handler + ) + + def generate_basic_feature_blame_experiment_actions( project: Project, bc_file_extensions: tp.Optional[tp.List[BCFileExtensions]] = None, diff --git a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py b/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py deleted file mode 100644 index d46d6353e..000000000 --- a/varats/varats/projects/cpp_projects/commit_feature_interactions_test_repo.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Projects in vara-test-repos used for testing the bug provider.""" -import typing as tp - -import benchbuild as bb -from plumbum import local - -from varats.paper.paper_config import PaperConfigSpecificGit -from varats.project.project_domain import ProjectDomains -from varats.project.project_util import ( - ProjectBinaryWrapper, - get_local_project_git_path, - BinaryType, -) -from varats.project.varats_project import VProject -from varats.utils.git_util import RevisionBinaryMap, ShortCommitHash - - -class CFI(VProject): # type: ignore - """test repo for commit-feature interactions.""" - NAME = 'CFI' # The name of the project - GROUP = 'cpp_projects' # The group this project belongs to - DOMAIN = ProjectDomains.TEST # The application domain of this project - - SOURCE = [ - PaperConfigSpecificGit( - project_name="CFI", - remote="https://github.com/sisteu56/cfi_test_repo.git", - local="CFI", - refspec="main", - limit=None, - shallow=False - ) - ] - - test_files = ["main.cpp", "helper.cpp", "dataflow.cpp", "more_dataflow.cpp"] - - @staticmethod - def binaries_for_revision( - revision: ShortCommitHash - ) -> tp.List[ProjectBinaryWrapper]: - """Return a list of binaries generated by the project.""" - binary_map = RevisionBinaryMap(get_local_project_git_path(CFI.NAME)) - for test_file in ["main", "helper"]: - binary_map.specify_binary(test_file, BinaryType.EXECUTABLE) - - return binary_map[revision] - - def run_tests(self) -> None: - """This function defines tests and benchmarks for the project.""" - - def compile(self) -> None: - """Contains instructions on how to build the project.""" - source = local.path(self.source_of_primary) - clang = bb.compiler.cxx(self) - with local.cwd(source): - for test_file in self.test_files: - bb.watch(clang)(test_file, "-o", test_file.replace('.cpp', '')) From 6c7e685859e32890a2f368bb9ef20fafca48a198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Mon, 11 Sep 2023 11:29:33 +0200 Subject: [PATCH 20/26] changes for checks --- varats/varats/data/reports/feature_blame_report.py | 7 +++---- .../experiments/vara/feature_blame_report_experiment.py | 2 +- varats/varats/tools/bb_config.py | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index 5517c37d6..11917bda2 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -7,7 +7,6 @@ import yaml from varats.base.version_header import VersionHeader -from varats.data.reports.blame_report import BlameReportMetaData from varats.data.reports.feature_analysis_report import ( FeatureAnalysisReportMetaData, ) @@ -97,7 +96,7 @@ def __init__(self, path: Path) -> None: self.__commit_feature_interactions.append(new_cfi) @property - def meta_data(self) -> FeatureBlameReportMetaData: + def meta_data(self) -> FeatureAnalysisReportMetaData: """Access the meta data that was gathered with the ``StructuralFeatureBlameReport``.""" return self.__meta_data @@ -164,7 +163,7 @@ def __init__(self, path: Path) -> None: version_header.raise_if_version_is_less_than(1) self.__meta_data = FeatureBlameReportMetaData \ - .create_blame_report_meta_data(next(documents)) + .create_feature_analysis_report_meta_data(next(documents)) self.__commit_feature_interactions: tp.List[ DataflowCommitFeatureInteraction] = [] @@ -178,7 +177,7 @@ def __init__(self, path: Path) -> None: self.__commit_feature_interactions.append(new_cfi) @property - def meta_data(self) -> FeatureBlameReportMetaData: + def meta_data(self) -> FeatureAnalysisReportMetaData: """Access the meta data that was gathered with the ``DataflowFeatureBlameReport``.""" return self.__meta_data diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index ec28f2ef4..67e797bfd 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -2,7 +2,7 @@ Implements the basic feature blame report experiment. The experiment analyses a project with VaRA's blame and feature analysis and -generates a FeatureBlameReport. +generates either a structural or dataflow-based FeatureBlameReport. """ import typing as tp diff --git a/varats/varats/tools/bb_config.py b/varats/varats/tools/bb_config.py index 51b2384c0..bbba500b8 100644 --- a/varats/varats/tools/bb_config.py +++ b/varats/varats/tools/bb_config.py @@ -74,8 +74,7 @@ def update_projects( 'varats.projects.cpp_projects.poppler', 'varats.projects.cpp_projects.z3', 'varats.projects.cpp_projects.ect', - 'varats.projects.cpp_projects.lepton', - 'varats.projects.cpp_projects.commit_feature_interactions_test_repo' + 'varats.projects.cpp_projects.lepton' ] projects_conf.value[:] += [ 'varats.projects.cpp_projects.doxygen', 'varats.projects.cpp_projects' From c0abddeb940253a5a9803a1b0f90cf7a06e0227a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Mon, 11 Sep 2023 14:43:39 +0200 Subject: [PATCH 21/26] added missing FeatureModelProvider to dataflow report --- .../vara/feature_blame_report_experiment.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index 67e797bfd..11b679912 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -38,8 +38,8 @@ class StructuralFeatureBlameReportGeneration( - actions.ProjectStep -): # type: ignore + actions.ProjectStep # type: ignore +): """Analyse a project with VaRA and generate a StructuralFeatureBlameReport.""" @@ -247,11 +247,23 @@ def actions_for_project( Args: project: to analyze """ + # FeatureModelProvider + fm_provider = FeatureModelProvider.create_provider_for_project(project) + if fm_provider is None: + raise FeatureModelNotFound(project, None) + + fm_path = fm_provider.get_feature_model_path(project) + + if fm_path is None or not fm_path.exists(): + raise FeatureModelNotFound(project, fm_path) # Try, to build the project without optimizations to get more precise # blame annotations. Note: this does not guarantee that a project is # build without optimizations because the used build tool/script can # still add optimizations flags after the experiment specified cflags. - project.cflags += ["-O1", "-Xclang", "-disable-llvm-optzns", "-g0"] + project.cflags += [ + f"-fvara-fm-path={fm_path.absolute()}", "-O1", "-Xclang", + "-disable-llvm-optzns", "-g0" + ] bc_file_extensions = [ BCFileExtensions.NO_OPT, BCFileExtensions.TBAA, BCFileExtensions.BLAME, BCFileExtensions.FEATURE From d542167f5da7e18e798dea111f7a80895d33141e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Mon, 11 Sep 2023 15:48:33 +0200 Subject: [PATCH 22/26] adapt test_bb_config for merge of experiments perviously merged blame_experiment and feature_blame_experiment into one file vara_experiments --- tests/utils/test_bb_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils/test_bb_config.py b/tests/utils/test_bb_config.py index 3b75c0fac..59657b25e 100644 --- a/tests/utils/test_bb_config.py +++ b/tests/utils/test_bb_config.py @@ -76,7 +76,7 @@ def test_if_experiments_were_added(self): "varats.experiments.discover_experiments", "varats.experiments.vara.region_instrumentation", "varats.experiments.vara.commit_annotation_report", - "varats.experiments.vara.blame_experiment", + "varats.experiments.vara.vara_experiments", "varats.experiments.vara.feature_experiment" ] From 519d96bd876adb1e2a71538fb10f37d5a62f126a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Thu, 14 Sep 2023 18:40:09 +0200 Subject: [PATCH 23/26] changes to review --- .../data/reports/feature_blame_report.py | 8 ------ .../vara/feature_blame_report_experiment.py | 4 +-- .../experiments/vara/vara_experiments.py | 26 ++----------------- 3 files changed, 4 insertions(+), 34 deletions(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index 11917bda2..6330a2f79 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -57,10 +57,6 @@ def commit(self) -> CommitRepoPair: """commit of this cfi.""" return self.__commit - def is_terminator(self) -> bool: - br_regex = re.compile(r'(br( i1 | label ))|(switch i\d{1,} )') - return br_regex.search(self.__feature) is not None - class FeatureBlameReportMetaData(FeatureAnalysisReportMetaData): pass @@ -142,10 +138,6 @@ def commit(self) -> tp.List[CommitRepoPair]: """commit of this cfi.""" return self.__commits - def is_terminator(self) -> bool: - br_regex = re.compile(r'(br( i1 | label ))|(switch i\d{1,} )') - return br_regex.search(self.__feature) is not None - class DataflowFeatureBlameReport( BaseReport, shorthand="DFBR", file_type="yaml" diff --git a/varats/varats/experiments/vara/feature_blame_report_experiment.py b/varats/varats/experiments/vara/feature_blame_report_experiment.py index 11b679912..48d08771c 100644 --- a/varats/varats/experiments/vara/feature_blame_report_experiment.py +++ b/varats/varats/experiments/vara/feature_blame_report_experiment.py @@ -146,7 +146,7 @@ def actions_for_project( FBE.setup_basic_feature_blame_experiment(self, project, SFBR) - analysis_actions = FBE.generate_basic_feature_blame_experiment_actions( + analysis_actions = FBE.generate_basic_blame_experiment_actions( project, bc_file_extensions, extraction_error_handler=create_default_compiler_error_handler( @@ -271,7 +271,7 @@ def actions_for_project( FBE.setup_basic_feature_blame_experiment(self, project, DFBR) - analysis_actions = FBE.generate_basic_feature_blame_experiment_actions( + analysis_actions = FBE.generate_basic_blame_experiment_actions( project, bc_file_extensions, extraction_error_handler=create_default_compiler_error_handler( diff --git a/varats/varats/experiments/vara/vara_experiments.py b/varats/varats/experiments/vara/vara_experiments.py index df78a6572..95b7386d0 100644 --- a/varats/varats/experiments/vara/vara_experiments.py +++ b/varats/varats/experiments/vara/vara_experiments.py @@ -78,8 +78,8 @@ def setup_basic_feature_blame_experiment( experiment.get_handle(), project, report_type ) - # This c-flag is provided by VaRA and it suggests to use the git-blame - # annotation. + # These flags are provided by VaRA and suggest to use git-blame + # and feature annotations. project.cflags += ["-fvara-GB", "-fvara-feature"] @@ -103,25 +103,3 @@ def generate_basic_blame_experiment_actions( return get_bc_cache_actions( project, bc_file_extensions, extraction_error_handler ) - - -def generate_basic_feature_blame_experiment_actions( - project: Project, - bc_file_extensions: tp.Optional[tp.List[BCFileExtensions]] = None, - extraction_error_handler: tp.Optional[PEErrorHandler] = None -) -> tp.List[actions.Step]: - """ - Generate the basic actions for a feature blame experiment. - - - handle caching of BC files - - compile project, if needed - - Args: - project: reference to the BB project - bc_file_extensions: list of bitcode file extensions (e.g. opt, no opt) - extraction_error_handler: handler to manage errors during the - extraction process - """ - return get_bc_cache_actions( - project, bc_file_extensions, extraction_error_handler - ) From dc5db5d12ffb8e1d1a4e3a06df10b4b394dc5437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Wed, 20 Sep 2023 18:18:31 +0200 Subject: [PATCH 24/26] remove unused import --- varats/varats/data/reports/feature_blame_report.py | 1 - 1 file changed, 1 deletion(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index 6330a2f79..912fd4e52 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -1,6 +1,5 @@ """Module for StructuralFeatureBlameReport and DataflowFeatureBlameReport.""" -import re import typing as tp from pathlib import Path From 42de982e151ecdbde201a6e524cc726d71b3dd46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Mon, 25 Sep 2023 17:28:43 +0200 Subject: [PATCH 25/26] adapt to changes in structural cfi collection some refactoring --- .../data/reports/feature_blame_report.py | 89 ++++++++++--------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index 912fd4e52..4fa3832c2 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -13,32 +13,35 @@ from varats.utils.git_util import CommitRepoPair -class StructuralCommitFeatureInteraction(): +class StructuralCommitFeatureInteraction: """A StructuralCommitFeatureInteraction detailing the specific commit-hash and repo and feature and the number of instructions this structural cfi occurs in.""" def __init__( - self, num_instructions: int, feature: str, commit: CommitRepoPair + self, num_instructions: int, features: tp.List[str], + commit: CommitRepoPair ) -> None: self.__num_instructions = num_instructions - self.__feature = feature + self.__features = features self.__commit = commit @staticmethod def create_commit_feature_interaction( raw_inst_entry: tp.Dict[str, tp.Any] - ) -> 'StructuralCommitFeatureInteraction': + ) -> "StructuralCommitFeatureInteraction": """Creates a `StructuralCommitFeatureInteraction` entry from the corresponding yaml document section.""" - num_instructions = int(raw_inst_entry['num-instructions']) - feature: str = str(raw_inst_entry['feature']) + num_instructions = int(raw_inst_entry["num-instructions"]) + features: tp.List[str] = [ + str(feature) for feature in raw_inst_entry["features"] + ] commit: CommitRepoPair = CommitRepoPair( - (raw_inst_entry['commit-repo-pair'])['commit'], - (raw_inst_entry['commit-repo-pair'])['repository'] + (raw_inst_entry["commit-repo-pair"])["commit"], + (raw_inst_entry["commit-repo-pair"])["repository"], ) return StructuralCommitFeatureInteraction( - num_instructions, feature, commit + num_instructions, features, commit ) @property @@ -47,9 +50,9 @@ def num_instructions(self) -> int: return self.__num_instructions @property - def feature(self) -> str: - """The feature of this cfi.""" - return self.__feature + def features(self) -> str: + """The features of this cfi.""" + return self.__features @property def commit(self) -> CommitRepoPair: @@ -70,25 +73,25 @@ class StructuralFeatureBlameReport( def __init__(self, path: Path) -> None: super().__init__(path) - with open(path, 'r') as stream: + with open(path, "r") as stream: documents = yaml.load_all(stream, Loader=yaml.CLoader) version_header = VersionHeader(next(documents)) version_header.raise_if_not_type("StructuralFeatureBlameReport") version_header.raise_if_version_is_less_than(1) - self.__meta_data = FeatureBlameReportMetaData \ - .create_feature_analysis_report_meta_data(next(documents)) + self.__meta_data = ( + FeatureBlameReportMetaData. + create_feature_analysis_report_meta_data(next(documents)) + ) - self.__commit_feature_interactions: tp.List[ - StructuralCommitFeatureInteraction] = [] raw_feature_blame_report = next(documents) - for cfi in raw_feature_blame_report[ - 'structural-commit-feature-interactions']: - new_cfi = ( - StructuralCommitFeatureInteraction. - create_commit_feature_interaction(cfi) - ) - self.__commit_feature_interactions.append(new_cfi) + + self.__commit_feature_interactions = [ + StructuralCommitFeatureInteraction. + create_commit_feature_interaction(cfi) + for cfi in raw_feature_blame_report[ + "structural-commit-feature-interactions"] + ] @property def meta_data(self) -> FeatureAnalysisReportMetaData: @@ -98,7 +101,7 @@ def meta_data(self) -> FeatureAnalysisReportMetaData: @property def commit_feature_interactions( - self + self, ) -> tp.List[StructuralCommitFeatureInteraction]: """Return all structural cfis.""" return self.__commit_feature_interactions @@ -107,7 +110,7 @@ def commit_feature_interactions( ##### DATAFLOW ##### -class DataflowCommitFeatureInteraction(): +class DataflowCommitFeatureInteraction: """A DataflowCommitFeatureInteraction detailing the specific commit-hash and repo and feature this dataflow-based cfi occurs in.""" @@ -118,13 +121,14 @@ def __init__(self, feature: str, commits: tp.List[CommitRepoPair]) -> None: @staticmethod def create_commit_feature_interaction( raw_inst_entry: tp.Dict[str, tp.Any] - ) -> 'DataflowCommitFeatureInteraction': + ) -> "DataflowCommitFeatureInteraction": """Creates a `DataflowCommitFeatureInteraction` entry from the corresponding yaml document section.""" - feature: str = str(raw_inst_entry['feature']) - crps: tp.List[CommitRepoPair] = [] - for crp in raw_inst_entry['commit-repo-pairs']: - crps.append(CommitRepoPair(crp['commit'], crp['repository'])) + feature: str = str(raw_inst_entry["feature"]) + crps: tp.List[CommitRepoPair] = [ + CommitRepoPair(crp["commit"], crp["repository"]) + for crp in raw_inst_entry["commit-repo-pairs"] + ] return DataflowCommitFeatureInteraction(feature, crps) @property @@ -147,25 +151,24 @@ class DataflowFeatureBlameReport( def __init__(self, path: Path) -> None: super().__init__(path) - with open(path, 'r') as stream: + with open(path, "r") as stream: documents = yaml.load_all(stream, Loader=yaml.CLoader) version_header = VersionHeader(next(documents)) version_header.raise_if_not_type("DataflowFeatureBlameReport") version_header.raise_if_version_is_less_than(1) - self.__meta_data = FeatureBlameReportMetaData \ - .create_feature_analysis_report_meta_data(next(documents)) + self.__meta_data = ( + FeatureBlameReportMetaData. + create_feature_analysis_report_meta_data(next(documents)) + ) - self.__commit_feature_interactions: tp.List[ - DataflowCommitFeatureInteraction] = [] raw_feature_blame_report = next(documents) - for cfi in raw_feature_blame_report[ - 'dataflow-commit-feature-interactions']: - new_cfi = ( - DataflowCommitFeatureInteraction. - create_commit_feature_interaction(cfi) - ) - self.__commit_feature_interactions.append(new_cfi) + + self.__commit_feature_interactions = [ + DataflowCommitFeatureInteraction. + create_commit_feature_interaction(cfi) for cfi in + raw_feature_blame_report["dataflow-commit-feature-interactions"] + ] @property def meta_data(self) -> FeatureAnalysisReportMetaData: From 5848c183f7a103adc751537aa3401c5fc7a94b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20R=C3=BCdiger=20Steuer?= Date: Mon, 25 Sep 2023 20:18:15 +0200 Subject: [PATCH 26/26] fix mistake in return type --- varats/varats/data/reports/feature_blame_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/varats/varats/data/reports/feature_blame_report.py b/varats/varats/data/reports/feature_blame_report.py index 4fa3832c2..a68aa826a 100644 --- a/varats/varats/data/reports/feature_blame_report.py +++ b/varats/varats/data/reports/feature_blame_report.py @@ -50,7 +50,7 @@ def num_instructions(self) -> int: return self.__num_instructions @property - def features(self) -> str: + def features(self) -> tp.List[str]: """The features of this cfi.""" return self.__features