Skip to content

Commit

Permalink
Add experiment CompareASTBlame
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonievonMann committed Feb 2, 2024
1 parent 2d42787 commit 756ee48
Show file tree
Hide file tree
Showing 3 changed files with 392 additions and 0 deletions.
152 changes: 152 additions & 0 deletions varats/varats/data/reports/blame_annotations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"""Module for BlameAnnotations, mapping instructions to blame information."""
import typing as tp
from pathlib import Path

import yaml

from varats.base.version_header import VersionHeader
from varats.report.report import BaseReport


class BlameInstruction():
"""Collection of debug blame and VaRA blame."""

def __init__(self, dbghash: str, varahash: str) -> None:
self.__dbghash = dbghash
self.__varahash = varahash

@staticmethod
def create_blame_instruction(
raw_entry: tp.Dict[str, tp.Any]
) -> 'BlameInstruction':
"""Creates a :class`BlameInstrucion`from the corresponding yaml document
section."""
dbghash = str(raw_entry['dbghash'])
varahash = str(raw_entry['varahash'])
return BlameInstruction(dbghash, varahash) #TODO name

Check warning on line 26 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / pylint

[pylint] varats/varats/data/reports/blame_annotations.py#L26 <511>

TODO name (fixme)
Raw output
varats/varats/data/reports/blame_annotations.py:26:53: W0511: TODO name (fixme)

@property
def name(self) -> str:
"""Name of the instruction."""
return self.__name

Check failure on line 31 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / mypy

[mypy] varats/varats/data/reports/blame_annotations.py#L31

error: Returning Any from function declared to return "str" [no-any-return]
Raw output
varats/varats/data/reports/blame_annotations.py:31:9: error: Returning Any from function declared to return "str"  [no-any-return]

Check failure on line 31 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / mypy

[mypy] varats/varats/data/reports/blame_annotations.py#L31

error: "BlameInstruction" has no attribute "__name"; maybe "name"? [attr-defined]
Raw output
varats/varats/data/reports/blame_annotations.py:31:16: error: "BlameInstruction" has no attribute "__name"; maybe "name"?  [attr-defined]

Check failure on line 31 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / pylint

[pylint] varats/varats/data/reports/blame_annotations.py#L31 <1101>

Instance of 'BlameInstruction' has no '__name' member (no-member)
Raw output
varats/varats/data/reports/blame_annotations.py:31:15: E1101: Instance of 'BlameInstruction' has no '__name' member (no-member)

@property
def dbghash(self) -> str:
"""Blame based on debug information."""
return self.__dbghash

@property
def varahash(self) -> str:
"""Blame based on IRegion."""
return self.__varahash


class BlameAnnotations(BaseReport, shorthand="BA", file_type="yaml"):
"""Report containing debug blame and blame annotations."""

def __init__(self, path: Path) -> None:
super().__init__(path)
self.__blame_annotations = {}

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("BlameAnnotations")

raw_blame_report = next(documents)

for raw_entry in raw_blame_report['annotations']:
new_entry = (
BlameInstruction.create_blame_instruction(
raw_blame_report['annotations'][raw_entry]
)
)
self.__blame_annotations[raw_entry] = new_entry

@property
def blame_annotations(self) -> tp.ValuesView[BlameInstruction]:
"""Iterate over all blame annotations."""
return self.__blame_annotations.values()


class ASTBlameReport(BaseReport, shorthand="BAST", file_type="yaml"):

Check failure on line 72 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / pylint

[pylint] varats/varats/data/reports/blame_annotations.py#L72 <902>

Too many instance attributes (8/7) (too-many-instance-attributes)
Raw output
varats/varats/data/reports/blame_annotations.py:72:0: R0902: Too many instance attributes (8/7) (too-many-instance-attributes)
"""Report containing difference between AST-based and line-based blame."""

def __init__(self, path: Path) -> None:
super().__init__(path)
self.__diff_dbg_ast = 0
self.__eq_dbg_ast = 0
self.__diff_line_ast = 0
self.__eq_line_ast = 0

def print_yaml(self) -> None:

Check failure on line 82 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / pylint

[pylint] varats/varats/data/reports/blame_annotations.py#L82 <116>

Missing function or method docstring (missing-function-docstring)
Raw output
varats/varats/data/reports/blame_annotations.py:82:4: C0116: Missing function or method docstring (missing-function-docstring)
data = {
'dbg vs ast': {
'diff': self.__diff_dbg_ast,
'equal': self.__eq_dbg_ast
},
'line vs ast': {
'diff': self.__diff_line_ast,
'equal': self.__eq_line_ast
}
}
with open(self.path, 'w') as yaml_file:
yaml.dump(data, yaml_file, default_flow_style=False)

@property
def diff_dbg_ast(self) -> int:
"""Count of different instructions between debug and ast blame."""
return self.__diff_dbg_ast

@diff_dbg_ast.setter
def diff_dbg_ast(self, value) -> None:

Check failure on line 102 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / mypy

[mypy] varats/varats/data/reports/blame_annotations.py#L102

error: Function is missing a type annotation for one or more arguments [no-untyped-def]
Raw output
varats/varats/data/reports/blame_annotations.py:102:5: error: Function is missing a type annotation for one or more arguments  [no-untyped-def]
self.__diff_dbg_ast = value

@property
def eq_dbg_ast(self) -> int:
"""Count of equal instructions between debug and ast blame."""
return self.__eq_dbg_ast

@eq_dbg_ast.setter
def eq_dbg_ast(self, value) -> None:

Check failure on line 111 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / mypy

[mypy] varats/varats/data/reports/blame_annotations.py#L111

error: Function is missing a type annotation for one or more arguments [no-untyped-def]
Raw output
varats/varats/data/reports/blame_annotations.py:111:5: error: Function is missing a type annotation for one or more arguments  [no-untyped-def]
self.__eq_dbg_ast = value

@property
def diff_line_ast(self) -> int:
"""Count of different instructions between line and ast blame."""
return self.__diff_line_ast

@diff_line_ast.setter
def diff_line_ast(self, value) -> None:

Check failure on line 120 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / mypy

[mypy] varats/varats/data/reports/blame_annotations.py#L120

error: Function is missing a type annotation for one or more arguments [no-untyped-def]
Raw output
varats/varats/data/reports/blame_annotations.py:120:5: error: Function is missing a type annotation for one or more arguments  [no-untyped-def]
self.__diff_line_ast = value

@property
def eq_line_ast(self) -> int:
"""Count of equal instructions between line and ast blame."""
return self.__eq_line_ast

@eq_line_ast.setter
def eq_line_ast(self, value) -> None:

Check failure on line 129 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / mypy

[mypy] varats/varats/data/reports/blame_annotations.py#L129

error: Function is missing a type annotation for one or more arguments [no-untyped-def]
Raw output
varats/varats/data/reports/blame_annotations.py:129:5: error: Function is missing a type annotation for one or more arguments  [no-untyped-def]
self.__eq_line_ast = value


def compare_blame_annotations(

Check failure on line 133 in varats/varats/data/reports/blame_annotations.py

View workflow job for this annotation

GitHub Actions / pylint

[pylint] varats/varats/data/reports/blame_annotations.py#L133 <116>

Missing function or method docstring (missing-function-docstring)
Raw output
varats/varats/data/reports/blame_annotations.py:133:0: C0116: Missing function or method docstring (missing-function-docstring)
line_ba: BlameAnnotations, ast_ba: BlameAnnotations, path: Path
) -> ASTBlameReport:
ast_report = ASTBlameReport(path)

for entry in ast_ba.blame_annotations:
if entry.dbghash == entry.varahash:
ast_report.eq_dbg_ast += 1
else:
ast_report.diff_dbg_ast += 1

for line_entry, ast_entry in zip(
line_ba.blame_annotations, ast_ba.blame_annotations
):
if line_entry.varahash == ast_entry.varahash:
ast_report.eq_line_ast += 1
else:
ast_report.diff_line_ast += 1

return ast_report
239 changes: 239 additions & 0 deletions varats/varats/experiments/vara/blame_ast_experiment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
"""
Implements the blame AST experiment.
The experiment compares AST based blame annotations to line based ones.
"""

import fnmatch
import os
import typing as tp

from benchbuild import Project
from benchbuild.utils import actions
from benchbuild.utils.cmd import opt

import varats.experiments.vara.blame_experiment as BE
from varats.data.reports.blame_annotations import ASTBlameReport as BAST
from varats.data.reports.blame_annotations import BlameAnnotations as BA
from varats.data.reports.blame_annotations import compare_blame_annotations
from varats.data.reports.blame_report import BlameReport as BR

Check warning on line 19 in varats/varats/experiments/vara/blame_ast_experiment.py

View workflow job for this annotation

GitHub Actions / pylint

[pylint] varats/varats/experiments/vara/blame_ast_experiment.py#L19 <611>

Unused BlameReport imported from varats.data.reports.blame_report as BR (unused-import)
Raw output
varats/varats/experiments/vara/blame_ast_experiment.py:19:0: W0611: Unused BlameReport imported from varats.data.reports.blame_report as BR (unused-import)
from varats.experiment.experiment_util import (
ExperimentHandle,
VersionExperiment,
create_default_analysis_failure_handler,
create_default_compiler_error_handler,
create_new_success_result_filepath,
exec_func_with_pe_error_handler,
get_varats_result_folder,
wrap_unlimit_stack_size,
)
from varats.experiment.wllvm import (
BCFileExtensions,
_create_default_bc_file_creation_actions,
get_cached_bc_file_path,
)
from varats.project.project_util import get_local_project_git_paths
from varats.project.varats_project import VProject
from varats.report.report import (
FileStatusExtension,
ReportFilename,
ReportFilepath,
ReportSpecification,
)
from varats.utils.git_util import ShortCommitHash


class BlameAnnotationGeneration(actions.ProjectStep): #type: ignore
"""Generate blame annotation report."""

NAME = "BlameAnnotationGeneration"
DESCRIPTION = "Generates report with debug and IInfo blame with -vara-BA of VaRA."

Check failure on line 50 in varats/varats/experiments/vara/blame_ast_experiment.py

View workflow job for this annotation

GitHub Actions / pylint

[pylint] varats/varats/experiments/vara/blame_ast_experiment.py#L50 <301>

Line too long (86/80) (line-too-long)
Raw output
varats/varats/experiments/vara/blame_ast_experiment.py:50:0: C0301: Line too long (86/80) (line-too-long)

project: VProject

def __init__(
self, project: Project, experiment_handle: ExperimentHandle,
file_prefix: str
):
super().__init__(project=project)
self.__experiment_handle = experiment_handle
self.__file_prefix = file_prefix

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-BA: to run a commit flow report
* -yaml-report-outfile=<path>: 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.
varats_result_folder = get_varats_result_folder(self.project)
result_filepath = ReportFilepath(
varats_result_folder,
ReportFilename(
self.__file_prefix + self.__experiment_handle.get_file_name(
BAST.shorthand(),
project_name=str(self.project.name),
binary_name=binary.name,
project_revision=ShortCommitHash(
self.project.version_of_primary
),
project_uuid=str(self.project.run_uuid),
extension_type=FileStatusExtension.SUCCESS,
config_id=None
).filename
)
)

opt_params = [
"--enable-new-pm=0", "-vara-BD", "-vara-BA",
"-vara-init-commits", "-vara-rewriteMD",
"-vara-git-mappings=" + ",".join([
f'{repo}:{path}' for repo, path in
get_local_project_git_paths(self.project.name).items()
]), "-vara-use-phasar",
f"-vara-report-outfile={result_filepath}",
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, BAST
)
)

return actions.StepResult.OK


class BlameASTComparison(actions.ProjectStep): #type: ignore
"""Compare BlameAnnotation reports of AST based annotations to line based
ones."""

NAME = "BlameASTComparison"
DESCRIPTION = "Compares BlameAnnotation reports of AST based annotations to line based ones."

Check failure on line 132 in varats/varats/experiments/vara/blame_ast_experiment.py

View workflow job for this annotation

GitHub Actions / pylint

[pylint] varats/varats/experiments/vara/blame_ast_experiment.py#L132 <301>

Line too long (97/80) (line-too-long)
Raw output
varats/varats/experiments/vara/blame_ast_experiment.py:132:0: C0301: Line too long (97/80) (line-too-long)

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:

Check failure on line 147 in varats/varats/experiments/vara/blame_ast_experiment.py

View workflow job for this annotation

GitHub Actions / pylint

[pylint] varats/varats/experiments/vara/blame_ast_experiment.py#L147 <116>

Missing function or method docstring (missing-function-docstring)
Raw output
varats/varats/experiments/vara/blame_ast_experiment.py:147:4: C0116: Missing function or method docstring (missing-function-docstring)
for binary in self.project.binaries:
varats_result_folder = get_varats_result_folder(self.project)

for file in os.listdir(varats_result_folder):
if fnmatch.fnmatch(file, "linereport" + '*'):
line_filepath = os.path.join(varats_result_folder, file)
if fnmatch.fnmatch(file, "astreport" + '*'):
ast_filepath = os.path.join(varats_result_folder, file)

line_annotations = BA(line_filepath)

Check failure on line 157 in varats/varats/experiments/vara/blame_ast_experiment.py

View workflow job for this annotation

GitHub Actions / mypy

[mypy] varats/varats/experiments/vara/blame_ast_experiment.py#L157

error: Argument 1 to "BlameAnnotations" has incompatible type "str"; expected "Path" [arg-type]
Raw output
varats/varats/experiments/vara/blame_ast_experiment.py:157:35: error: Argument 1 to "BlameAnnotations" has incompatible type "str"; expected "Path"  [arg-type]
ast_annotations = BA(ast_filepath)

Check failure on line 158 in varats/varats/experiments/vara/blame_ast_experiment.py

View workflow job for this annotation

GitHub Actions / mypy

[mypy] varats/varats/experiments/vara/blame_ast_experiment.py#L158

error: Argument 1 to "BlameAnnotations" has incompatible type "str"; expected "Path" [arg-type]
Raw output
varats/varats/experiments/vara/blame_ast_experiment.py:158:34: error: Argument 1 to "BlameAnnotations" has incompatible type "str"; expected "Path"  [arg-type]

result_file = create_new_success_result_filepath(
self.__experiment_handle, BAST, self.project, binary
)

ast_report = compare_blame_annotations(
line_annotations, ast_annotations, result_file.full_path()
)

ast_report.print_yaml()

return actions.StepResult.OK


class BlameASTExperiment(VersionExperiment, shorthand="BASTE"):
"""Compares AST based blame annotations to line based ones."""

NAME = "CompareASTBlame"

REPORT_SPEC = ReportSpecification(BAST)

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", "-g"]
bc_file_extensions = [
BCFileExtensions.NO_OPT,
BCFileExtensions.TBAA,
BCFileExtensions.BLAME,
]

BE.setup_basic_blame_experiment(self, project, BAST)
# Compile with line based blame annotations
analysis_actions = _create_default_bc_file_creation_actions(
project,
bc_file_extensions if bc_file_extensions else [],
extraction_error_handler=create_default_compiler_error_handler(
self.get_handle(), project, self.REPORT_SPEC.main_report
)
)
# Generate blame annotation report
analysis_actions.append(
BlameAnnotationGeneration(
project, self.get_handle(), "linereport-"
)
)

# Compile with AST based blame annotations
project.cflags += ["-fvara-ast-GB"]
analysis_actions.extend(
_create_default_bc_file_creation_actions(
project,
bc_file_extensions if bc_file_extensions else [],
extraction_error_handler=create_default_compiler_error_handler(
self.get_handle(), project, self.REPORT_SPEC.main_report
)
)
)

# Generate blame annotation report
analysis_actions.append(
BlameAnnotationGeneration(project, self.get_handle(), "astreport-")
)

# Generate AST blame report (comparison)
analysis_actions.append(BlameASTComparison(project, self.get_handle()))

analysis_actions.append(actions.Clean(project))

return analysis_actions
Loading

0 comments on commit 756ee48

Please sign in to comment.