Compare commits

..

2 Commits

Author SHA1 Message Date
34eba970cc Initial analyzer/summarizer commit 2021-03-04 02:37:12 +02:00
0509d250ab Better for-loop/fallthrough branch detection. 2021-03-04 02:36:12 +02:00
8 changed files with 299 additions and 1 deletions

140
.gitignore vendored
View File

@ -34,3 +34,143 @@
# Build folders
[Bb]uild/
cmake-*/
## Python begins here.
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

View File

@ -30,6 +30,7 @@ void OperationFinder::processArithmetic(const clang::BinaryOperator* op, const c
log.line = line_number;
log.operation = getOpcode(op).str();
log.current_for_loops = _for_loop_stack;
log.is_fallthrough = _in_fallthrough;
_processExpressionTypes(log, op, op->getLHS(), op->getRHS());
@ -56,6 +57,7 @@ void OperationFinder::processUnaryArithmetic(const clang::UnaryOperator* op, con
log.line = line_number;
log.operation = getOpcode(op).str();
log.current_for_loops = _for_loop_stack;
log.is_fallthrough = _in_fallthrough;
_processExpressionTypes(log, op, lhs, nullptr);
@ -64,6 +66,16 @@ void OperationFinder::processUnaryArithmetic(const clang::UnaryOperator* op, con
llvm::outs() << "\n";
}
void OperationFinder::fallthroughBranchEntered()
{
_in_fallthrough = true;
}
void OperationFinder::fallthroughBranchExited()
{
_in_fallthrough = false;
}
void OperationFinder::forLoopEntered()
{
_for_loop_stack.push_back(_next_for_loop_id);

View File

@ -17,6 +17,9 @@ public:
void processArithmetic(const clang::BinaryOperator* op, const clang::SourceManager& source_manager);
void processUnaryArithmetic(const clang::UnaryOperator* op, const clang::SourceManager& source_manager);
void fallthroughBranchEntered();
void fallthroughBranchExited();
void forLoopEntered();
void forLoopExited();
@ -24,6 +27,7 @@ private:
void _processExpressionTypes(OperationLog& log, const clang::Expr* source, const clang::Expr* op1, const clang::Expr* op2);
int _next_for_loop_id = 0;
bool _in_fallthrough = false;
std::vector<int> _for_loop_stack;
IOperationOutput* _storage;
};

View File

@ -52,6 +52,7 @@ bool OperationFinderAstVisitor::dataTraverseStmtPre(clang::Stmt* stmt)
if (loop->getInit())
{
_loop_init = loop->getInit();
_op_finder->fallthroughBranchEntered();
}
else
{
@ -69,6 +70,7 @@ bool OperationFinderAstVisitor::dataTraverseStmtPost(clang::Stmt* stmt)
if (_loop_init && _loop_init == stmt)
{
_op_finder->forLoopEntered();
_op_finder->fallthroughBranchExited();
_loop_init = nullptr;
}
else if (clang::dyn_cast<clang::ForStmt>(stmt))

View File

@ -14,6 +14,7 @@ struct OperationLog
std::string operand_lhs;
std::string operand_rhs;
std::string operand_result;
bool is_fallthrough;
std::vector<int> current_for_loops;
};
@ -35,6 +36,7 @@ inline void to_json(nlohmann::json& j, const OperationLog& l)
{"operand_lhs", l.operand_lhs},
{"operand_rhs", l.operand_rhs},
{"operand_result", l.operand_result},
{"is_fallthrough", l.is_fallthrough},
{"current_for_loops", l.current_for_loops}
};
}
@ -46,6 +48,7 @@ inline void from_json(const nlohmann::json& j, OperationLog& l)
j.at("operand_lhs").get_to(l.operand_lhs);
j.at("operand_rhs").get_to(l.operand_rhs);
j.at("operand_result").get_to(l.operand_result);
j.at("is_fallthrough").get_to(l.is_fallthrough);
j.at("current_for_loops").get_to(l.current_for_loops);
}

View File

@ -0,0 +1,36 @@
import json
from dataclasses import dataclass
from typing import Dict, List
@dataclass
class GCovLine:
line_number: int
count: int
is_fallthrough: bool = False
class GCovFile:
def __init__(self, path: str) -> None:
self._path = path
self._data: dict = {}
self.files: Dict[str, List[GCovLine]] = {}
def read(self) -> None:
with open(self._path, "r") as infile:
self._data = json.load(infile)
for file in self._data["files"]:
name: str = file["file"]
lines: List[GCovLine] = []
for line in file["lines"]:
if len(line["branches"]):
for branch in line["branches"]:
lines.append(GCovLine(line["line_number"], branch["count"], branch["fallthrough"]))
else:
lines.append(GCovLine(line["line_number"], line["count"]))
self.files[name] = lines

View File

@ -0,0 +1,58 @@
from dataclasses import dataclass
from typing import Dict, List, Optional
from gcovreader import GCovFile, GCovLine
from opfinderreader import OperationLogReader, OperationLog
@dataclass(frozen=True)
class UniqueOperation:
name: str
type_lhs: Optional[str]
type_rhs: Optional[str]
type_expr: Optional[str]
@staticmethod
def from_operation_log(op_log: OperationLog) -> "UniqueOperation":
return UniqueOperation(
op_log.operation,
op_log.operand_lhs,
op_log.operand_rhs,
op_log.operand_result
)
if __name__ == "__main__":
gcov = GCovFile("./data/gcov.json")
ops = OperationLogReader("./data/opfinder.json")
gcov.read()
ops.read()
for file_name in gcov.files:
op_counter: Dict[UniqueOperation, int] = {}
if file_name not in ops.files:
print(f"Couldn't find {file_name} in op-finder output. Skipping.")
continue
loop_stack: List[int] = []
for gcov_line in gcov.files[file_name]:
op_lines = ops.get_lines(file_name, gcov_line.line_number)
for opfinder_line in op_lines:
# TODO: revise this. Need a special case for for-loop clauses
# or branching in general.
if opfinder_line.is_fallthrough != gcov_line.is_fallthrough:
continue
unique_op = UniqueOperation.from_operation_log(opfinder_line)
if unique_op in op_counter:
op_counter[unique_op] += gcov_line.count
else:
op_counter[unique_op] = gcov_line.count
print(f"Unique operations for file {file_name}:")
for uop, count in op_counter.items():
print(f"\t{count}: {uop}")

View File

@ -0,0 +1,43 @@
import json
from dataclasses import dataclass, field
from typing import Dict, List
@dataclass
class OperationLog:
operation: str
line: int
operand_lhs: str
operand_rhs: str
operand_result: str
is_fallthrough: bool
current_for_loops: list[int] = field(default_factory=list)
class OperationLogReader:
def __init__(self, path: str) -> None:
self._path = path
self._data: dict = {}
self.files: Dict[str, List[OperationLog]] = {}
def read(self) -> None:
with open(self._path, "r") as infile:
self._data = json.load(infile)
for name, ops_list in self._data.items():
ops: List[OperationLog] = []
for op_json in ops_list:
ops.append(OperationLog(**op_json))
self.files[name] = ops
def get_lines(self, file: str, line_number: int) -> List[OperationLog]:
res: List[OperationLog] = []
for line in self.files[file]:
if line.line == line_number:
res.append(line)
return res