Compare commits
1 Commits
master
...
testing/re
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bc37e23ae |
@ -1,66 +0,0 @@
|
||||
# Generated from CLion C/C++ Code Style settings
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignOperands: true
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Always
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: true
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterUnion: true
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeComma
|
||||
ColumnLimit: 0
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 8
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 2
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PointerAlignment: Left
|
||||
ReflowComments: false
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 0
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 2
|
||||
UseTab: Never
|
||||
36
.drone.yml
36
.drone.yml
@ -1,36 +0,0 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: build and test
|
||||
pull: never
|
||||
image: erki/llvm:latest
|
||||
commands:
|
||||
- apt install --no-install-recommends python3-pip -y
|
||||
- pip3 install --upgrade conan
|
||||
- conan profile new default --detect
|
||||
- conan profile update settings.compiler.libcxx=libstdc++11 default
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- conan install .. --build=missing
|
||||
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=ON
|
||||
- ninja
|
||||
- ctest . --output-on-failure
|
||||
- name: opsummarizer integration tests
|
||||
pull: never
|
||||
image: erki/llvm:latest
|
||||
commands:
|
||||
- apt install --no-install-recommends gcovr python3-pip -y
|
||||
- pip3 install --upgrade conan
|
||||
- conan profile new default --detect
|
||||
- conan profile update settings.compiler.libcxx=libstdc++11 default
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- conan install .. --build=missing
|
||||
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=OFF
|
||||
- ninja op-finder
|
||||
- cp -r ../testcases ./testcases
|
||||
- cd ./testcases
|
||||
- ln ../op-finder/op-finder ./op-finder
|
||||
- python3 ../../op-summarizer/tests.py
|
||||
144
.gitignore
vendored
144
.gitignore
vendored
@ -1,7 +1,3 @@
|
||||
## Project specifics
|
||||
testcases/*/
|
||||
|
||||
## C & C++
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
@ -38,143 +34,3 @@ testcases/*/
|
||||
# Build folders
|
||||
[Bb]uild/
|
||||
cmake-*/
|
||||
|
||||
## Python.
|
||||
# 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/
|
||||
|
||||
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@ -1,8 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
1
.idea/.name
generated
1
.idea/.name
generated
@ -1 +0,0 @@
|
||||
C Analyzer
|
||||
2
.idea/analyzer.git.iml
generated
2
.idea/analyzer.git.iml
generated
@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||
59
.idea/codeStyles/Project.xml
generated
59
.idea/codeStyles/Project.xml
generated
@ -1,59 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<Objective-C>
|
||||
<option name="INDENT_NAMESPACE_MEMBERS" value="0" />
|
||||
<option name="INDENT_C_STRUCT_MEMBERS" value="2" />
|
||||
<option name="INDENT_CLASS_MEMBERS" value="2" />
|
||||
<option name="INDENT_INSIDE_CODE_BLOCK" value="2" />
|
||||
<option name="NAMESPACE_BRACE_PLACEMENT" value="2" />
|
||||
<option name="FUNCTION_BRACE_PLACEMENT" value="2" />
|
||||
<option name="BLOCK_BRACE_PLACEMENT" value="2" />
|
||||
<option name="SUPERCLASS_LIST_COMMA_ON_NEXT_LINE" value="true" />
|
||||
<option name="SPACE_WITHIN_EMPTY_BRACES" value="true" />
|
||||
<option name="SPACE_BEFORE_POINTER_IN_DECLARATION" value="false" />
|
||||
<option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" />
|
||||
<option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
|
||||
<option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
|
||||
</Objective-C>
|
||||
<Objective-C-extensions>
|
||||
<extensions>
|
||||
<pair source="cpp" header="hpp" fileNamingConvention="PASCAL_CASE" />
|
||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="cu" header="cuh" fileNamingConvention="NONE" />
|
||||
</extensions>
|
||||
<rules>
|
||||
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
|
||||
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="STRUCT" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="CLASS_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
|
||||
<rule entity="CLASS_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
|
||||
<rule entity="STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
|
||||
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
|
||||
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
|
||||
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
|
||||
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
|
||||
<rule entity="CLASS_MEMBER_FIELD,STRUCT_MEMBER_FIELD" visibility="PROTECTED,PRIVATE" specifier="ANY" prefix="_" style="SNAKE_CASE" suffix="" />
|
||||
<rule entity="CLASS_MEMBER_FUNCTION,STRUCT_MEMBER_FUNCTION" visibility="PROTECTED,PRIVATE" specifier="ANY" prefix="_" style="PASCAL_CASE" suffix="" />
|
||||
</rules>
|
||||
</Objective-C-extensions>
|
||||
<codeStyleSettings language="CMake">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="ObjectiveC">
|
||||
<option name="BRACE_STYLE" value="2" />
|
||||
<option name="CLASS_BRACE_STYLE" value="2" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
4
.idea/misc.xml
generated
4
.idea/misc.xml
generated
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/analyzer.git.iml" filepath="$PROJECT_DIR$/.idea/analyzer.git.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -1,16 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
project("C Analyzer")
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter")
|
||||
|
||||
option(WITH_TESTS "Build unit tests as well." OFF)
|
||||
|
||||
add_subdirectory(op-finder-lib)
|
||||
add_subdirectory(op-finder)
|
||||
|
||||
if(WITH_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(op-finder-tests)
|
||||
endif()
|
||||
|
||||
16
Dockerfile
16
Dockerfile
@ -1,16 +0,0 @@
|
||||
FROM ubuntu:20.04
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
RUN apt-get update -y
|
||||
RUN apt-get install -y git build-essential cmake ninja-build python3 python3-pip
|
||||
|
||||
RUN git clone --depth=1 https://github.com/llvm/llvm-project.git
|
||||
|
||||
RUN mkdir /llvm-project/build
|
||||
WORKDIR /llvm-project/build
|
||||
RUN cmake ../llvm -G"Ninja" -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=Release
|
||||
RUN ninja install
|
||||
|
||||
WORKDIR /
|
||||
RUN rm -r /llvm-project
|
||||
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
MIT License Copyright (c) 2021 Erki Meinberg
|
||||
MIT License Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
124
README.md
124
README.md
@ -1,124 +1,2 @@
|
||||
# clang Based Atomic Operations Parser & Analyzer
|
||||
# masters-thesis
|
||||
|
||||
The source code attached for a master's thesis work carried out in 2021.
|
||||
Contained within is the source code for an **Atomic Operations Parser** and
|
||||
a **Performance Analyzer**.
|
||||
|
||||
## Atomic Operations Parser (Operatioon Finder)
|
||||
|
||||
The **Atomic Operations Parser** is a C++ program which is meant to parse C-code
|
||||
source files, and extract atomic operations from them. The tool uses LLVM & clang.
|
||||
So precompiling the development libraries of those two is required.
|
||||
|
||||
### Building LLVM & clang
|
||||
|
||||
A docker file which automatically compiles and installs the required dependencies
|
||||
is included for convenience.
|
||||
|
||||
Otherwise, the steps to installing clang are as follows:
|
||||
|
||||
Install the required dependencies via apt:
|
||||
```shell
|
||||
apt update -y
|
||||
apt install -y git build-essential cmake ninja-build python3 python3-pip
|
||||
```
|
||||
|
||||
Clone LLVM from the repo. Depth 1 makes the process faster. Also set up the various
|
||||
folders for building and installing.
|
||||
```shell
|
||||
cd ~
|
||||
git clone --branch "release/11.x" --depth 1 https://github.com/llvm/llvm-project.git
|
||||
mkdir ~/llvm-project/build
|
||||
mkdir ~/llvm-install
|
||||
cd ~/llvm-project/build
|
||||
```
|
||||
|
||||
Run cmake to configure the project. Followed by ninja to install it.
|
||||
Note that when installing, you can modify `-DCMAKE_INSTALL_PREFIX` to specify
|
||||
where the libraries should be installed to. In this case, we'll put them into
|
||||
`~/llvm-install`.
|
||||
```shell
|
||||
cmake ../llvm -G "Ninja" -DCMAKE_INSTALL_PREFIX=~/llvm-install -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=Release
|
||||
ninja install
|
||||
```
|
||||
|
||||
### Building the Tool
|
||||
|
||||
Using the conan package manager is recommended. Otherwise you have to provide `Catch2_ROOT`
|
||||
and `nlohmann_json_ROOT` yourself.
|
||||
|
||||
The variables `Clang_ROOT` and `LLVM_ROOT` depend on the previous step. If you installed the libraries
|
||||
into your system, then you don't need to specify them. Otherwise, assuming an installation directory of
|
||||
`~/llvm_install`, they'd look as follows:
|
||||
```
|
||||
-DClang_ROOT=~/llvm_install/lib/cmake/clang/
|
||||
-DLLVM_ROOT=~/llvm_install/lib/cmake/llvm/
|
||||
```
|
||||
|
||||
Now clone this repo and `cd` inside of it. Make a build directory and build the project:
|
||||
|
||||
```shell
|
||||
mkdir build
|
||||
cd build
|
||||
conan install .. --build=missing
|
||||
export CLANG_ROOT=~/llvm_install/lib/cmake/clang/
|
||||
export LLVM_ROOT=~/llvm_install/lib/cmake/llvm/
|
||||
cmake .. -DWITH_TESTS=ON -DClang_ROOT=${CLANG_ROOT} -DLLVM_ROOT=${LLVM_ROOT} -GNinja
|
||||
ninja
|
||||
```
|
||||
|
||||
You are now left with `op-finder/op-finder` and `op-finder-tests/op-finder-tests` executables.
|
||||
|
||||
### Usage
|
||||
|
||||
Use `op-finder --help` for help.
|
||||
|
||||
The finder will process multiple source files and output them to a single JSON file. For example:
|
||||
|
||||
`op-finder ./source1.c ./source2.c -o=project_opfinder.json`
|
||||
|
||||
The above line will take the C-code source files of `source1.c` and `source2.c`, extract atomic operations
|
||||
from them, and output the JSON to the `./project_opfinder.json` file. This file can then be given to the
|
||||
analyzer along with a gcov report.
|
||||
|
||||
## Analyzer (Operation Summarizer)
|
||||
|
||||
The Analyzer is responsible for taking the Atomic Operations Finder report and a gcov code coverage report
|
||||
and combining them into a singular analysis of the codebase. In the present implementation, it will
|
||||
summarize all unique atomic operations. This can then be combined with a database of atomic operations
|
||||
and turned into a performance estimation.
|
||||
|
||||
The Analyzer is written in Python and requires no tooling beyond having Python 3 installed. gcov is needed
|
||||
to generate the simulation reports.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Install the prerequisites from your package manager:
|
||||
```shell
|
||||
apt update -y
|
||||
apt install gcovr python3 python3-pip
|
||||
pip3 install gcovr
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
Assuming Atomic Operations Parser was used in the previous usage example. The first step is to compile
|
||||
the C program with GCC and to acquire a code coverage report from it using gcovr. This is done as follows:
|
||||
```shell
|
||||
gcc -fprofile-arcs -ftest-coverage -fPIC -O0 ./source1.c ./source2.c -o project.out
|
||||
./project.out
|
||||
gcovr -r ./ --json-pretty -o project_gcov.json
|
||||
```
|
||||
|
||||
This will output the coverage report in human-readable JSON into the `project_gcov.json` file.
|
||||
|
||||
Next, the Analyzer needs to be ran:
|
||||
```shell
|
||||
python3 op-summarizer/opsummarizer.py --gcov project_gcov.json --finder project_opfinder.json --output project_summarized.json source1.c source2.c
|
||||
```
|
||||
|
||||
The list of files in the specifies which source files should be taken into consideration. If a file is not present, then
|
||||
that file will not be evaluated during the summarization.
|
||||
|
||||
The summarizer will then generate an output report in JSON, along with printing a human-readable version out
|
||||
on the screen.
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
[requires]
|
||||
catch2/2.13.4
|
||||
nlohmann_json/3.9.1
|
||||
|
||||
[generators]
|
||||
cmake_find_package
|
||||
@ -1,49 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
find_package(Clang REQUIRED)
|
||||
find_package(LLVM REQUIRED COMPONENTS Support Option Core)
|
||||
|
||||
# LLVM is typically compiled without RTTI. Weird linker errors ensue if
|
||||
# you keep RTTI on and try to link.
|
||||
if (NOT LLVM_ENABLE_RTTI)
|
||||
if (MSVC)
|
||||
string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
|
||||
else ()
|
||||
string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
llvm_map_components_to_libnames(llvm_libs support option core)
|
||||
|
||||
add_library(op-finder-lib STATIC
|
||||
src/OperationFinder.cpp
|
||||
src/OperationStorage.cpp
|
||||
src/OperationAstMatcher.cpp
|
||||
src/OperationFinderAstVisitor.cpp
|
||||
src/OperationFinderAstConsumer.cpp
|
||||
src/OperationFinderAstAction.cpp
|
||||
src/OperationLog.cpp)
|
||||
|
||||
target_include_directories(op-finder-lib
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${LLVM_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_definitions(op-finder-lib
|
||||
PUBLIC
|
||||
${LLVM_DEFINITIONS}
|
||||
)
|
||||
|
||||
target_link_libraries(op-finder-lib
|
||||
PUBLIC
|
||||
nlohmann_json::nlohmann_json
|
||||
PRIVATE
|
||||
${llvm_libs}
|
||||
clangTooling
|
||||
clangBasic
|
||||
clangASTMatchers
|
||||
)
|
||||
@ -1,25 +0,0 @@
|
||||
//
|
||||
// Created by erki on 02.03.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_OPERATIONASTMATCHER_HPP
|
||||
#define C_ANALYZER_OPERATIONASTMATCHER_HPP
|
||||
|
||||
#include <clang/ASTMatchers/ASTMatchFinder.h>
|
||||
|
||||
class OperationFinder;
|
||||
|
||||
class OperationASTMatcher : public clang::ast_matchers::MatchFinder::MatchCallback
|
||||
{
|
||||
public:
|
||||
explicit OperationASTMatcher(OperationFinder* finder);
|
||||
|
||||
void addToFinder(clang::ast_matchers::MatchFinder& finder);
|
||||
|
||||
void run(const clang::ast_matchers::MatchFinder::MatchResult& result) override;
|
||||
private:
|
||||
OperationFinder* _op_finder;
|
||||
};
|
||||
|
||||
|
||||
#endif //C_ANALYZER_OPERATIONASTMATCHER_HPP
|
||||
@ -1,35 +0,0 @@
|
||||
//
|
||||
// Created by erki on 16.02.21.
|
||||
//
|
||||
|
||||
#ifndef LLVM_PROTO_OPERATIONFINDER_HPP
|
||||
#define LLVM_PROTO_OPERATIONFINDER_HPP
|
||||
|
||||
#include <clang/ASTMatchers/ASTMatchFinder.h>
|
||||
|
||||
#include "OperationLog.hpp"
|
||||
|
||||
class OperationFinder
|
||||
{
|
||||
public:
|
||||
explicit OperationFinder(IOperationOutput* storage);
|
||||
|
||||
void processArithmetic(const clang::BinaryOperator* op, const clang::SourceManager& source_manager);
|
||||
void processUnaryArithmetic(const clang::UnaryOperator* op, const clang::SourceManager& source_manager);
|
||||
void processFunctionCall(const clang::CallExpr* call, const clang::SourceManager& source_manager);
|
||||
void processArraySubscript(const clang::ArraySubscriptExpr* subscript, const clang::SourceManager& source_manager);
|
||||
|
||||
void branchEntered();
|
||||
void branchExited();
|
||||
|
||||
private:
|
||||
std::unique_ptr<OperationLog::BasicOperation>
|
||||
_createBasicOperationLogEntry(const std::string& opcode, const clang::Expr* source, const clang::Expr* op1, const clang::Expr* op2);
|
||||
|
||||
std::pair<std::string, OperationLog> _createBaseOperationLog(const clang::Stmt* stmt, const clang::SourceManager& source_manager);
|
||||
|
||||
int _current_branch = 0;
|
||||
IOperationOutput* _storage;
|
||||
};
|
||||
|
||||
#endif //LLVM_PROTO_OPERATIONFINDER_HPP
|
||||
@ -1,24 +0,0 @@
|
||||
//
|
||||
// Created by erki on 02.03.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_OPERATIONFINDERASTACTION_HPP
|
||||
#define C_ANALYZER_OPERATIONFINDERASTACTION_HPP
|
||||
|
||||
#include <clang/Frontend/CompilerInstance.h>
|
||||
#include <clang/Frontend/FrontendActions.h>
|
||||
|
||||
class OperationFinder;
|
||||
|
||||
class OperationFinderAstAction
|
||||
{
|
||||
public:
|
||||
explicit OperationFinderAstAction(OperationFinder* op_finder);
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer> newASTConsumer();
|
||||
private:
|
||||
OperationFinder* _op_finder;
|
||||
};
|
||||
|
||||
|
||||
#endif //C_ANALYZER_OPERATIONFINDERASTACTION_HPP
|
||||
@ -1,27 +0,0 @@
|
||||
//
|
||||
// Created by erki on 02.03.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_OPERATIONFINDERASTCONSUMER_HPP
|
||||
#define C_ANALYZER_OPERATIONFINDERASTCONSUMER_HPP
|
||||
|
||||
#include <clang/AST/ASTConsumer.h>
|
||||
|
||||
#include "OperationFinderAstVisitor.hpp"
|
||||
|
||||
class OperationFinder;
|
||||
|
||||
class OperationFinderAstConsumer : public clang::ASTConsumer
|
||||
{
|
||||
public:
|
||||
explicit OperationFinderAstConsumer(OperationFinder* op_finder);
|
||||
|
||||
void Initialize(clang::ASTContext& context) override;
|
||||
|
||||
void HandleTranslationUnit(clang::ASTContext& context) override;
|
||||
|
||||
private:
|
||||
OperationFinderAstVisitor _visitor;
|
||||
};
|
||||
|
||||
#endif //C_ANALYZER_OPERATIONFINDERASTCONSUMER_HPP
|
||||
@ -1,48 +0,0 @@
|
||||
//
|
||||
// Created by erki on 02.03.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_OPERATIONFINDERASTVISITOR_HPP
|
||||
#define C_ANALYZER_OPERATIONFINDERASTVISITOR_HPP
|
||||
|
||||
#include <clang/AST/RecursiveASTVisitor.h>
|
||||
|
||||
class OperationFinder;
|
||||
|
||||
class OperationFinderAstVisitor
|
||||
: public clang::RecursiveASTVisitor<OperationFinderAstVisitor>
|
||||
{
|
||||
public:
|
||||
explicit OperationFinderAstVisitor(OperationFinder* op_finder);
|
||||
|
||||
void NewContext(clang::ASTContext* context);
|
||||
|
||||
bool VisitForStmt(clang::ForStmt* stmt);
|
||||
bool VisitBinaryOperator(clang::BinaryOperator* op);
|
||||
bool VisitUnaryOperator(clang::UnaryOperator* op);
|
||||
bool VisitCallExpr(clang::CallExpr* call);
|
||||
bool VisitArraySubscriptExpr(clang::ArraySubscriptExpr* subscript);
|
||||
|
||||
bool dataTraverseStmtPre(clang::Stmt* stmt);
|
||||
bool dataTraverseStmtPost(clang::Stmt* stmt);
|
||||
|
||||
private:
|
||||
clang::ASTContext* _context;
|
||||
OperationFinder* _op_finder;
|
||||
|
||||
struct _LoopHeaderStateMachine
|
||||
{
|
||||
bool in_loop_header = false;
|
||||
clang::Stmt* init = nullptr;
|
||||
clang::Stmt* header_start = nullptr;
|
||||
clang::Stmt* header_end = nullptr;
|
||||
};
|
||||
|
||||
_LoopHeaderStateMachine _loop_header;
|
||||
|
||||
std::vector<clang::Stmt*> _branch_stack;
|
||||
|
||||
clang::Stmt* _isBranchEntry(clang::Stmt* stmt);
|
||||
};
|
||||
|
||||
#endif //C_ANALYZER_OPERATIONFINDERASTVISITOR_HPP
|
||||
@ -1,114 +0,0 @@
|
||||
//
|
||||
// Created by erki on 28.02.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_OPERATIONLOG_HPP
|
||||
#define C_ANALYZER_OPERATIONLOG_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
struct OperationLog
|
||||
{
|
||||
struct IEntry
|
||||
{
|
||||
[[nodiscard]] virtual nlohmann::json toJson() const = 0;
|
||||
};
|
||||
|
||||
struct FunctionCall : IEntry
|
||||
{
|
||||
static constexpr char TYPE_NAME[] = "function_call";
|
||||
|
||||
std::string function_name;
|
||||
std::string call_result_type;
|
||||
|
||||
[[nodiscard]] nlohmann::json toJson() const override;
|
||||
};
|
||||
|
||||
struct BasicOperation : IEntry
|
||||
{
|
||||
static constexpr char TYPE_NAME[] = "basic_operation";
|
||||
|
||||
std::string operation_name;
|
||||
std::string type_lhs;
|
||||
std::string type_rhs;
|
||||
std::string type_result;
|
||||
|
||||
[[nodiscard]] nlohmann::json toJson() const override;
|
||||
};
|
||||
|
||||
unsigned int line = 0;
|
||||
int branch_number = 0;
|
||||
|
||||
std::string entry_type;
|
||||
std::unique_ptr<IEntry> entry;
|
||||
|
||||
OperationLog() = default;
|
||||
OperationLog(const OperationLog&) = delete;
|
||||
OperationLog(OperationLog&&) = default;
|
||||
|
||||
void DecodeEntry(const nlohmann::json& j);
|
||||
};
|
||||
|
||||
class IOperationOutput
|
||||
{
|
||||
public:
|
||||
virtual ~IOperationOutput() = default;
|
||||
|
||||
virtual void pushOperation(const std::string& filename, OperationLog&& op) = 0;
|
||||
};
|
||||
|
||||
inline void to_json(nlohmann::json& j, const OperationLog& l)
|
||||
{
|
||||
j = nlohmann::json{
|
||||
{"line", l.line},
|
||||
{"entry_type", l.entry_type},
|
||||
{"entry", l.entry->toJson()},
|
||||
{"branch_number", l.branch_number}
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, OperationLog& l)
|
||||
{
|
||||
j.at("line").get_to(l.line);
|
||||
j.at("entry_type").get_to(l.entry_type);
|
||||
l.DecodeEntry(j["entry"]);
|
||||
|
||||
j.at("branch_number").get_to(l.branch_number);
|
||||
}
|
||||
|
||||
inline void to_json(nlohmann::json& j, const OperationLog::BasicOperation& bo)
|
||||
{
|
||||
j = nlohmann::json{
|
||||
{"operation_name", bo.operation_name},
|
||||
{"type_lhs", bo.type_lhs},
|
||||
{"type_rhs", bo.type_rhs},
|
||||
{"type_result", bo.type_result}
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, OperationLog::BasicOperation& bo)
|
||||
{
|
||||
j.at("operation_name").get_to(bo.operation_name);
|
||||
j.at("type_lhs").get_to(bo.type_lhs);
|
||||
j.at("type_rhs").get_to(bo.type_rhs);
|
||||
j.at("type_result").get_to(bo.type_result);
|
||||
}
|
||||
|
||||
inline void to_json(nlohmann::json& j, const OperationLog::FunctionCall& fcall)
|
||||
{
|
||||
j = nlohmann::json{
|
||||
{"function_name", fcall.function_name},
|
||||
{"call_result_type", fcall.call_result_type}
|
||||
};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, OperationLog::FunctionCall& fcall)
|
||||
{
|
||||
j.at("function_name").get_to(fcall.function_name);
|
||||
j.at("call_result_type").get_to(fcall.call_result_type);
|
||||
}
|
||||
|
||||
#endif //C_ANALYZER_OPERATIONLOG_HPP
|
||||
@ -1,36 +0,0 @@
|
||||
//
|
||||
// Created by erki on 28.02.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_OPERATIONSTORAGE_HPP
|
||||
#define C_ANALYZER_OPERATIONSTORAGE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "OperationLog.hpp"
|
||||
|
||||
class OperationStorage : public IOperationOutput
|
||||
{
|
||||
public:
|
||||
OperationStorage() = default;
|
||||
OperationStorage(const OperationStorage&) = delete;
|
||||
|
||||
~OperationStorage() override;
|
||||
|
||||
void enablePrettyPrint();
|
||||
void toStream(std::ostream& stream);
|
||||
void toFile(const std::string& output_filename);
|
||||
|
||||
void pushOperation(const std::string& original_filename, OperationLog&& op) override;
|
||||
[[nodiscard]] const std::unordered_map<std::string, std::vector<OperationLog>>& getOperations() const;
|
||||
private:
|
||||
std::unordered_map<std::string, std::vector<OperationLog>> _operations;
|
||||
|
||||
bool _pretty_print = false;
|
||||
|
||||
std::string _convertFilepath(const std::string& original);
|
||||
};
|
||||
|
||||
#endif //C_ANALYZER_OPERATIONSTORAGE_HPP
|
||||
@ -1,52 +0,0 @@
|
||||
//src
|
||||
// Created by erki on 02.03.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_ASTHELPERS_HPP
|
||||
#define C_ANALYZER_ASTHELPERS_HPP
|
||||
|
||||
#include <clang/AST/ASTContext.h>
|
||||
|
||||
template<typename TOp>
|
||||
clang::StringRef getOpcode(const TOp *op)
|
||||
{
|
||||
return op->getOpcodeStr(op->getOpcode());
|
||||
}
|
||||
|
||||
inline clang::SourceLocation resolveOperationSourceLocation(const clang::SourceManager& source_manager,
|
||||
const clang::SourceLocation& original)
|
||||
{
|
||||
if (source_manager.isMacroBodyExpansion(original))
|
||||
{
|
||||
return source_manager.getExpansionLoc(original);
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
inline std::tuple<std::string, unsigned int, unsigned int> resolveLocationsWithLoc(const clang::SourceLocation& loc,
|
||||
const clang::SourceManager& source_manager)
|
||||
{
|
||||
const auto& loc_resolved = resolveOperationSourceLocation(source_manager, loc);
|
||||
|
||||
return {
|
||||
source_manager.getFilename(loc_resolved).str(),
|
||||
source_manager.getSpellingLineNumber(loc_resolved),
|
||||
source_manager.getSpellingColumnNumber(loc_resolved)
|
||||
};
|
||||
}
|
||||
|
||||
template<typename TStmt>
|
||||
std::tuple<std::string, unsigned int, unsigned int> resolveLocations(const TStmt* op,
|
||||
const clang::SourceManager& source_manager)
|
||||
{
|
||||
const auto& loc = resolveOperationSourceLocation(source_manager, op->getBeginLoc());
|
||||
|
||||
return {
|
||||
source_manager.getFilename(loc).str(),
|
||||
source_manager.getSpellingLineNumber(loc),
|
||||
source_manager.getSpellingColumnNumber(loc)
|
||||
};
|
||||
}
|
||||
|
||||
#endif //C_ANALYZER_ASTHELPERS_HPP
|
||||
@ -1,57 +0,0 @@
|
||||
//
|
||||
// Created by erki on 02.03.21.
|
||||
//
|
||||
|
||||
#include "OperationAstMatcher.hpp"
|
||||
|
||||
#include "OperationFinder.hpp"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
StatementMatcher AssignmentMatcher =
|
||||
binaryOperation(isAssignmentOperator(),
|
||||
hasLHS(expr().bind("lhs")),
|
||||
hasRHS(expr().bind("rhs"))).bind("assignment");
|
||||
|
||||
StatementMatcher ArithmeticMatcher =
|
||||
binaryOperation(hasAnyOperatorName("+", "-", "/", "*", "<", ">",
|
||||
"<=", ">=", "==", "<<", ">>", "%"),
|
||||
hasLHS(expr().bind("lhs")),
|
||||
hasRHS(expr().bind("lhs"))).bind("arithmetic");
|
||||
|
||||
StatementMatcher UnaryArithmeticMatcher =
|
||||
unaryOperator(hasAnyOperatorName("!", "++", "--"),
|
||||
hasUnaryOperand(expr().bind("lhs"))).bind("unary_arithmetic");
|
||||
|
||||
}
|
||||
|
||||
OperationASTMatcher::OperationASTMatcher(OperationFinder* finder)
|
||||
: _op_finder(finder)
|
||||
{ }
|
||||
|
||||
void OperationASTMatcher::addToFinder(clang::ast_matchers::MatchFinder& finder)
|
||||
{
|
||||
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, ArithmeticMatcher), this);
|
||||
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, AssignmentMatcher), this);
|
||||
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, UnaryArithmeticMatcher), this);
|
||||
}
|
||||
|
||||
void OperationASTMatcher::run(const clang::ast_matchers::MatchFinder::MatchResult& result)
|
||||
{
|
||||
if (const auto* op = result.Nodes.getNodeAs<clang::BinaryOperator>("assignment"))
|
||||
{
|
||||
_op_finder->processArithmetic(op, *result.SourceManager);
|
||||
}
|
||||
else if (const auto* op = result.Nodes.getNodeAs<clang::BinaryOperator>("arithmetic"))
|
||||
{
|
||||
_op_finder->processArithmetic(op, *result.SourceManager);
|
||||
}
|
||||
else if (const auto* op = result.Nodes.getNodeAs<clang::UnaryOperator>("unary_arithmetic"))
|
||||
{
|
||||
_op_finder->processUnaryArithmetic(op, *result.SourceManager);
|
||||
}
|
||||
}
|
||||
@ -1,170 +0,0 @@
|
||||
//
|
||||
// Created by erki on 16.02.21.
|
||||
//
|
||||
|
||||
#include "OperationFinder.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "ASTHelpers.hpp"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string ResolveTypeName(const QualType& t)
|
||||
{
|
||||
if (t->isTypedefNameType())
|
||||
{
|
||||
const TypedefType* tdt = cast<TypedefType>(t);
|
||||
assert(tdt);
|
||||
llvm::outs() << "Typedef type: " << t.getAsString()
|
||||
<< ", underlying: " << tdt->desugar().getAsString()
|
||||
<< ".";
|
||||
|
||||
return tdt->desugar().getAsString();
|
||||
}
|
||||
else if (t->isBuiltinType())
|
||||
{
|
||||
llvm::outs() << "Builtin type: " << t.getAsString() << ".";
|
||||
return t.getAsString();
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm::outs() << "Other type: " << t.getAsString() << ".";
|
||||
return t.getAsString();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
OperationFinder::OperationFinder(IOperationOutput* storage)
|
||||
: _storage(storage)
|
||||
{
|
||||
assert(storage);
|
||||
}
|
||||
|
||||
void OperationFinder::processArithmetic(const clang::BinaryOperator* op, const clang::SourceManager& source_manager)
|
||||
{
|
||||
auto [file_name, log] = _createBaseOperationLog(op, source_manager);
|
||||
const std::string op_code = getOpcode(op).str();
|
||||
|
||||
llvm::outs() << "\n\tBinary arithmetic: Type: " << op_code;
|
||||
|
||||
log.entry_type = OperationLog::BasicOperation::TYPE_NAME;
|
||||
log.entry = _createBasicOperationLogEntry(op_code, op, op->getLHS(), op->getRHS());
|
||||
|
||||
_storage->pushOperation(file_name, std::move(log));
|
||||
|
||||
llvm::outs() << "\n";
|
||||
}
|
||||
|
||||
void OperationFinder::processUnaryArithmetic(const clang::UnaryOperator* op, const clang::SourceManager& source_manager)
|
||||
{
|
||||
auto [file_name, log] = _createBaseOperationLog(op, source_manager);
|
||||
const std::string op_code = getOpcode(op).str();
|
||||
|
||||
llvm::outs() << "\n\tUnary arithmetic: Type: " << op_code;
|
||||
|
||||
log.entry_type = OperationLog::BasicOperation::TYPE_NAME;
|
||||
log.entry = _createBasicOperationLogEntry(op_code, op, op->getExprStmt(), nullptr);
|
||||
|
||||
_storage->pushOperation(file_name, std::move(log));
|
||||
|
||||
llvm::outs() << "\n";
|
||||
}
|
||||
|
||||
void OperationFinder::processFunctionCall(const clang::CallExpr* call, const SourceManager& source_manager)
|
||||
{
|
||||
const FunctionDecl* func = call->getDirectCallee();
|
||||
|
||||
assert(func);
|
||||
|
||||
auto [file_name, log] = _createBaseOperationLog(call, source_manager);
|
||||
const std::string func_name = func->getNameAsString();
|
||||
|
||||
llvm::outs() << "\n\tFunction call: func name: " << func_name << "\n\tResult eval type: ";
|
||||
|
||||
const std::string res_type = ResolveTypeName(func->getReturnType());
|
||||
|
||||
auto func_call = std::make_unique<OperationLog::FunctionCall>();
|
||||
func_call->function_name = func_name;
|
||||
func_call->call_result_type = res_type;
|
||||
|
||||
log.entry_type = OperationLog::FunctionCall::TYPE_NAME;
|
||||
log.entry = std::move(func_call);
|
||||
|
||||
_storage->pushOperation(file_name, std::move(log));
|
||||
|
||||
llvm::outs() << "\n";
|
||||
}
|
||||
|
||||
void OperationFinder::processArraySubscript(const clang::ArraySubscriptExpr* subscript, const clang::SourceManager& source_manager)
|
||||
{
|
||||
auto [file_name, log] = _createBaseOperationLog(subscript, source_manager);
|
||||
|
||||
llvm::outs() << "\n\tSubscript:";
|
||||
|
||||
log.entry_type = OperationLog::BasicOperation::TYPE_NAME;
|
||||
log.entry = _createBasicOperationLogEntry("subscript", subscript, subscript->getBase(), subscript->getIdx());
|
||||
|
||||
_storage->pushOperation(file_name, std::move(log));
|
||||
|
||||
llvm::outs() << "\n";
|
||||
}
|
||||
|
||||
void OperationFinder::branchEntered()
|
||||
{
|
||||
_current_branch++;
|
||||
llvm::outs() << "Branch entered: " << _current_branch << "\n";
|
||||
}
|
||||
|
||||
void OperationFinder::branchExited()
|
||||
{
|
||||
llvm::outs() << "Branch exited: " << _current_branch << "\n";
|
||||
|
||||
_current_branch--;
|
||||
|
||||
assert(_current_branch > -1);
|
||||
}
|
||||
|
||||
std::unique_ptr<OperationLog::BasicOperation>
|
||||
OperationFinder::_createBasicOperationLogEntry(const std::string& opcode, const Expr* source, const Expr* op1, const Expr* op2)
|
||||
{
|
||||
auto log = std::make_unique<OperationLog::BasicOperation>();
|
||||
|
||||
log->operation_name = opcode;
|
||||
|
||||
llvm::outs() << "\n\tExpression eval type: ";
|
||||
log->type_result = ResolveTypeName(source->getType());
|
||||
|
||||
if (op1)
|
||||
{
|
||||
llvm::outs() << "\n\tLHS eval type: ";
|
||||
log->type_lhs = ResolveTypeName(op1->getType());
|
||||
}
|
||||
if (op2)
|
||||
{
|
||||
llvm::outs() << "\n\tRHS eval type: ";
|
||||
log->type_rhs = ResolveTypeName(op2->getType());
|
||||
}
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
std::pair<std::string, OperationLog> OperationFinder::_createBaseOperationLog(const clang::Stmt* stmt, const clang::SourceManager& source_manager)
|
||||
{
|
||||
const auto [file_name, line_number, column_number] = resolveLocations(stmt, source_manager);
|
||||
|
||||
OperationLog log;
|
||||
log.line = line_number;
|
||||
log.branch_number = _current_branch;
|
||||
|
||||
llvm::outs() << file_name << ":"
|
||||
<< line_number << ":"
|
||||
<< column_number << ":";
|
||||
|
||||
return { file_name, std::move(log) };
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
//
|
||||
// Created by erki on 02.03.21.
|
||||
//
|
||||
|
||||
#include "OperationFinderAstAction.hpp"
|
||||
|
||||
#include "OperationFinderAstConsumer.hpp"
|
||||
|
||||
OperationFinderAstAction::OperationFinderAstAction(OperationFinder* op_finder)
|
||||
: _op_finder(op_finder)
|
||||
{ }
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer> OperationFinderAstAction::newASTConsumer()
|
||||
{
|
||||
return std::unique_ptr<clang::ASTConsumer>(
|
||||
new OperationFinderAstConsumer(_op_finder));
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
//
|
||||
// Created by erki on 02.03.21.
|
||||
//
|
||||
|
||||
#include "OperationFinderAstConsumer.hpp"
|
||||
|
||||
OperationFinderAstConsumer::OperationFinderAstConsumer(OperationFinder* op_finder)
|
||||
: _visitor(op_finder)
|
||||
{ }
|
||||
|
||||
void OperationFinderAstConsumer::Initialize(clang::ASTContext& context)
|
||||
{
|
||||
_visitor.NewContext(&context);
|
||||
}
|
||||
|
||||
void OperationFinderAstConsumer::HandleTranslationUnit(clang::ASTContext& context)
|
||||
{
|
||||
_visitor.TraverseAST(context);
|
||||
}
|
||||
@ -1,154 +0,0 @@
|
||||
//
|
||||
// Created by erki on 02.03.21.
|
||||
//
|
||||
|
||||
#include "OperationFinderAstVisitor.hpp"
|
||||
|
||||
#include "OperationFinder.hpp"
|
||||
#include "ASTHelpers.hpp"
|
||||
|
||||
OperationFinderAstVisitor::OperationFinderAstVisitor(OperationFinder* op_finder)
|
||||
: _context(nullptr)
|
||||
, _op_finder(op_finder)
|
||||
{ }
|
||||
|
||||
void OperationFinderAstVisitor::NewContext(clang::ASTContext* context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
bool OperationFinderAstVisitor::VisitForStmt(clang::ForStmt* stmt)
|
||||
{
|
||||
assert(_context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OperationFinderAstVisitor::VisitBinaryOperator(clang::BinaryOperator* op)
|
||||
{
|
||||
assert(_context);
|
||||
|
||||
_op_finder->processArithmetic(op, _context->getSourceManager());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OperationFinderAstVisitor::VisitUnaryOperator(clang::UnaryOperator* op)
|
||||
{
|
||||
assert(_context);
|
||||
|
||||
_op_finder->processUnaryArithmetic(op, _context->getSourceManager());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OperationFinderAstVisitor::VisitCallExpr(clang::CallExpr* call)
|
||||
{
|
||||
assert(_context);
|
||||
|
||||
_op_finder->processFunctionCall(call, _context->getSourceManager());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OperationFinderAstVisitor::VisitArraySubscriptExpr(clang::ArraySubscriptExpr* subscript)
|
||||
{
|
||||
assert(_context);
|
||||
|
||||
_op_finder->processArraySubscript(subscript, _context->getSourceManager());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OperationFinderAstVisitor::dataTraverseStmtPre(clang::Stmt* stmt)
|
||||
{
|
||||
assert(_context);
|
||||
|
||||
if (clang::Stmt* branch_entry = _isBranchEntry(stmt))
|
||||
{
|
||||
_branch_stack.push_back(branch_entry);
|
||||
_op_finder->branchEntered();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OperationFinderAstVisitor::dataTraverseStmtPost(clang::Stmt* stmt)
|
||||
{
|
||||
assert(_context);
|
||||
|
||||
if (_loop_header.in_loop_header && (_loop_header.init == stmt || _loop_header.header_end == stmt))
|
||||
{
|
||||
if (_loop_header.header_end == stmt)
|
||||
{
|
||||
assert(_branch_stack.back() == stmt);
|
||||
_branch_stack.pop_back();
|
||||
_op_finder->branchExited();
|
||||
|
||||
if (_loop_header.init)
|
||||
{
|
||||
assert(_branch_stack.back() == _loop_header.init);
|
||||
_branch_stack.pop_back();
|
||||
_op_finder->branchExited();
|
||||
}
|
||||
|
||||
_loop_header = {};
|
||||
}
|
||||
else if (_loop_header.init == stmt && !_loop_header.header_end)
|
||||
{
|
||||
assert(_branch_stack.back() == stmt);
|
||||
_branch_stack.pop_back();
|
||||
_op_finder->branchExited();
|
||||
|
||||
_loop_header = {};
|
||||
}
|
||||
}
|
||||
else if (!_branch_stack.empty() && _branch_stack.back() == stmt)
|
||||
{
|
||||
_branch_stack.pop_back();
|
||||
_op_finder->branchExited();
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(std::find(_branch_stack.cbegin(), _branch_stack.cend(), stmt)
|
||||
== _branch_stack.cend());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
clang::Stmt* OperationFinderAstVisitor::_isBranchEntry(clang::Stmt* stmt)
|
||||
{
|
||||
if (auto* loop = clang::dyn_cast<clang::ForStmt>(stmt);
|
||||
loop && loop->getInit() && (loop->getInc() || loop->getCond()))
|
||||
{
|
||||
_loop_header.init = loop->getInit();
|
||||
|
||||
if (loop->getCond())
|
||||
{
|
||||
_loop_header.header_start = loop->getCond();
|
||||
_loop_header.header_end = loop->getCond();
|
||||
}
|
||||
|
||||
if (loop->getInc())
|
||||
{
|
||||
if (!_loop_header.header_start)
|
||||
_loop_header.header_start = loop->getInc();
|
||||
|
||||
_loop_header.header_end = loop->getInc();
|
||||
}
|
||||
|
||||
_loop_header.in_loop_header = _loop_header.init || _loop_header.header_end;
|
||||
|
||||
if (_loop_header.init)
|
||||
return _loop_header.init;
|
||||
else if (_loop_header.header_start)
|
||||
return _loop_header.header_end;
|
||||
}
|
||||
else if (_loop_header.in_loop_header && _loop_header.header_start == stmt)
|
||||
{
|
||||
return _loop_header.header_end;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
//
|
||||
// Created by erki on 06.03.21.
|
||||
//
|
||||
|
||||
#include "OperationLog.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
std::unique_ptr<T> DecodeType(const nlohmann::json& j, const std::string& entry_type)
|
||||
{
|
||||
if (entry_type == T::TYPE_NAME)
|
||||
{
|
||||
auto t = std::make_unique<T>();
|
||||
j.get_to(*t);
|
||||
|
||||
return t;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
nlohmann::json OperationLog::FunctionCall::toJson() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
nlohmann::json OperationLog::BasicOperation::toJson() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OperationLog::DecodeEntry(const nlohmann::json& j)
|
||||
{
|
||||
if ((entry = DecodeType<BasicOperation>(j, entry_type)))
|
||||
return;
|
||||
else if ((entry = DecodeType<FunctionCall>(j, entry_type)))
|
||||
return;
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
//
|
||||
// Created by erki on 28.02.21.
|
||||
//
|
||||
|
||||
#include "OperationStorage.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include <llvm/Support/CommandLine.h>
|
||||
|
||||
OperationStorage::~OperationStorage()
|
||||
{ }
|
||||
|
||||
void OperationStorage::enablePrettyPrint()
|
||||
{
|
||||
_pretty_print = true;
|
||||
}
|
||||
|
||||
void OperationStorage::toStream(std::ostream& stream)
|
||||
{
|
||||
nlohmann::json json = _operations;
|
||||
|
||||
if (_pretty_print)
|
||||
stream << std::setw(4) << json;
|
||||
else
|
||||
stream << json;
|
||||
}
|
||||
|
||||
void OperationStorage::toFile(const std::string& output_filename)
|
||||
{
|
||||
std::ofstream file(output_filename);
|
||||
|
||||
toStream(file);
|
||||
}
|
||||
|
||||
|
||||
void OperationStorage::pushOperation(const std::string& original_filename, OperationLog&& op)
|
||||
{
|
||||
const std::string filename = _convertFilepath(original_filename);
|
||||
auto it = _operations.find(filename);
|
||||
|
||||
if (it == _operations.end())
|
||||
it = _operations.emplace(filename, std::vector<OperationLog>()).first;
|
||||
|
||||
it->second.emplace_back(std::move(op));
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, std::vector<OperationLog>>& OperationStorage::getOperations() const
|
||||
{
|
||||
return _operations;
|
||||
}
|
||||
|
||||
std::string OperationStorage::_convertFilepath(const std::string& original)
|
||||
{
|
||||
const std::filesystem::path path = original;
|
||||
|
||||
return path.filename().string();
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
find_package(Catch2 REQUIRED)
|
||||
|
||||
# LLVM is typically compiled without RTTI. Weird linker errors ensue if
|
||||
# you keep RTTI on and try to link.
|
||||
if (NOT LLVM_ENABLE_RTTI)
|
||||
if (MSVC)
|
||||
string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
|
||||
else ()
|
||||
string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
add_executable(op-finder-tests
|
||||
main.cpp
|
||||
fixtures/RunOnCodeFixture.cpp
|
||||
basic_operations.cpp
|
||||
branches.cpp
|
||||
function_calls.cpp)
|
||||
|
||||
target_link_libraries(op-finder-tests
|
||||
PUBLIC
|
||||
op-finder-lib
|
||||
Catch2::Catch2)
|
||||
|
||||
include(CTest)
|
||||
include(Catch)
|
||||
catch_discover_tests(op-finder-tests)
|
||||
@ -1,95 +0,0 @@
|
||||
//
|
||||
// Created by erki on 07.03.21.
|
||||
//
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "fixtures/RunOnCodeFixture.hpp"
|
||||
|
||||
TEST_CASE("Finds binary operations", "[basic_operation]")
|
||||
{
|
||||
auto binary_operand = GENERATE(std::string("="), std::string("+"), std::string("-"), std::string("/"),
|
||||
std::string("*"), std::string("<<"), std::string(">>"),
|
||||
std::string("^"), std::string("=="), std::string("|"), std::string("&"));
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; (void)(a " + binary_operand + " 4); }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 1);
|
||||
|
||||
const OperationLog& log = operations.front();
|
||||
|
||||
REQUIRE(log.entry_type == OperationLog::BasicOperation::TYPE_NAME);
|
||||
|
||||
const OperationLog::BasicOperation* op = (OperationLog::BasicOperation*)(log.entry.get());
|
||||
|
||||
REQUIRE(op->operation_name == binary_operand);
|
||||
REQUIRE(op->type_lhs == "int");
|
||||
REQUIRE(op->type_rhs == "int");
|
||||
REQUIRE(op->type_result == "int");
|
||||
}
|
||||
|
||||
TEST_CASE("Find unary operations", "[basic_operation]")
|
||||
{
|
||||
auto unary_operand = GENERATE(std::string("++"), std::string("--"), std::string("~"), std::string("!"));
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; (void)(" + unary_operand + "a); }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 1);
|
||||
|
||||
const OperationLog& log = operations.front();
|
||||
|
||||
REQUIRE(log.entry_type == OperationLog::BasicOperation::TYPE_NAME);
|
||||
|
||||
const OperationLog::BasicOperation* op = (OperationLog::BasicOperation*)(log.entry.get());
|
||||
|
||||
REQUIRE(op->operation_name == unary_operand);
|
||||
REQUIRE(op->type_lhs == "int");
|
||||
REQUIRE(op->type_rhs.empty());
|
||||
REQUIRE(op->type_result == "int");
|
||||
}
|
||||
|
||||
TEST_CASE("Find subscript operation", "[basic_operation]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a[4]; (void)a[4]; }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 1);
|
||||
|
||||
const OperationLog& log = operations.front();
|
||||
|
||||
REQUIRE(log.entry_type == OperationLog::BasicOperation::TYPE_NAME);
|
||||
|
||||
const OperationLog::BasicOperation* op = (OperationLog::BasicOperation*)(log.entry.get());
|
||||
|
||||
REQUIRE(op->operation_name == "subscript");
|
||||
REQUIRE(op->type_lhs == "int *");
|
||||
REQUIRE(op->type_rhs == "int");
|
||||
REQUIRE(op->type_result == "int");
|
||||
}
|
||||
|
||||
TEST_CASE("Find subscript operation reversed", "[basic_operation]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a[4]; (void)4[a]; }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 1);
|
||||
|
||||
const OperationLog& log = operations.front();
|
||||
|
||||
REQUIRE(log.entry_type == OperationLog::BasicOperation::TYPE_NAME);
|
||||
|
||||
const OperationLog::BasicOperation* op = (OperationLog::BasicOperation*)(log.entry.get());
|
||||
|
||||
REQUIRE(op->operation_name == "subscript");
|
||||
REQUIRE(op->type_lhs == "int *");
|
||||
REQUIRE(op->type_rhs == "int");
|
||||
REQUIRE(op->type_result == "int");
|
||||
}
|
||||
@ -1,125 +0,0 @@
|
||||
//
|
||||
// Created by erki on 07.03.21.
|
||||
//
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "fixtures/RunOnCodeFixture.hpp"
|
||||
|
||||
TEST_CASE("For loop without header.", "[branches][for_loops]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; for (;;) {} }";
|
||||
REQUIRE(fixture.runCode(code));
|
||||
|
||||
REQUIRE(fixture.storage.getOperations().empty());
|
||||
}
|
||||
|
||||
TEST_CASE("For loop with init only.", "[branches][for_loops]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; for (a = 4;;) {} }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 1);
|
||||
|
||||
const OperationLog& log = operations.front();
|
||||
|
||||
REQUIRE(log.branch_number == 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("For loop with init & cond.", "[branches][for_loops]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; for (a = 4; a < 4;) {} }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 2);
|
||||
|
||||
const OperationLog& log_init = operations.at(0);
|
||||
REQUIRE(log_init.branch_number == 1);
|
||||
|
||||
const OperationLog& log_cond = operations.at(1);
|
||||
REQUIRE(log_cond.branch_number == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("For loop with init & inc.", "[branches][for_loops]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; for (a = 4;;a++) {} }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 2);
|
||||
|
||||
const OperationLog& log_init = operations.at(0);
|
||||
REQUIRE(log_init.branch_number == 1);
|
||||
|
||||
const OperationLog& log_inc = operations.at(1);
|
||||
REQUIRE(log_inc.branch_number == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("For loop with full header.", "[branches][for_loops]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; for (a = 4; a < 4 ;a++) {} }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 3);
|
||||
|
||||
const OperationLog& log_init = operations.at(0);
|
||||
REQUIRE(log_init.branch_number == 1);
|
||||
|
||||
const OperationLog& log_cond = operations.at(1);
|
||||
REQUIRE(log_cond.branch_number == 2);
|
||||
|
||||
const OperationLog& log_inc = operations.at(2);
|
||||
REQUIRE(log_inc.branch_number == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("For loop without init.", "[branches][for_loops]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; for (;a < 4 ;a++) {} }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 2);
|
||||
|
||||
const OperationLog& log_cond = operations.at(0);
|
||||
REQUIRE(log_cond.branch_number == 0);
|
||||
|
||||
const OperationLog& log_inc = operations.at(1);
|
||||
REQUIRE(log_inc.branch_number == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("For loop closes branches inside the loop.", "[branches][for_loops]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; for (a = 4; a < 4 ; a++) { a = 5; } }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 4);
|
||||
|
||||
const OperationLog& log_inner = operations.back();
|
||||
REQUIRE(log_inner.branch_number == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("For loop closes branches outside the loop.", "[branches][for_loops]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int main() { int a; for (a = 4; a < 4 ; a++) {} a = 5; }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 4);
|
||||
|
||||
const OperationLog& log_outer = operations.back();
|
||||
REQUIRE(log_outer.branch_number == 0);
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
//
|
||||
// Created by erki on 07.03.21.
|
||||
//
|
||||
|
||||
#include "RunOnCodeFixture.hpp"
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <clang/Tooling/Tooling.h>
|
||||
#include <clang/Frontend/FrontendActions.h>
|
||||
|
||||
RunOnCodeFixture::RunOnCodeFixture()
|
||||
: finder(&storage)
|
||||
, action(&finder)
|
||||
{ }
|
||||
|
||||
bool RunOnCodeFixture::runCode(const std::string& code)
|
||||
{
|
||||
return clang::tooling::runToolOnCode(
|
||||
clang::tooling::newFrontendActionFactory(&action)->create(),
|
||||
code,
|
||||
RunOnCodeFixture::INPUT_FILE
|
||||
);
|
||||
}
|
||||
|
||||
const std::vector<OperationLog>& RunOnCodeFixture::operator()(const std::string& code)
|
||||
{
|
||||
const bool success = runCode(code);
|
||||
|
||||
REQUIRE(success);
|
||||
|
||||
const auto& operations = storage.getOperations();
|
||||
|
||||
REQUIRE(operations.count(RunOnCodeFixture::INPUT_FILE) == 1);
|
||||
|
||||
return operations.at(RunOnCodeFixture::INPUT_FILE);
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
//
|
||||
// Created by erki on 07.03.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_RUNONCODEFIXTURE_HPP
|
||||
#define C_ANALYZER_RUNONCODEFIXTURE_HPP
|
||||
|
||||
#include <OperationStorage.hpp>
|
||||
#include <OperationFinder.hpp>
|
||||
#include <OperationFinderAstAction.hpp>
|
||||
|
||||
struct RunOnCodeFixture
|
||||
{
|
||||
constexpr static char INPUT_FILE[] = "input.c";
|
||||
|
||||
OperationStorage storage;
|
||||
OperationFinder finder;
|
||||
OperationFinderAstAction action;
|
||||
|
||||
RunOnCodeFixture();
|
||||
|
||||
bool runCode(const std::string& code);
|
||||
|
||||
const std::vector<OperationLog>& operator()(const std::string& code);
|
||||
};
|
||||
|
||||
#endif //C_ANALYZER_RUNONCODEFIXTURE_HPP
|
||||
@ -1,55 +0,0 @@
|
||||
//
|
||||
// Created by erki on 07.03.21.
|
||||
//
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "fixtures/RunOnCodeFixture.hpp"
|
||||
|
||||
TEST_CASE("Find function call.", "[functions]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int b(); int main() { b(); }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 1);
|
||||
|
||||
const OperationLog& log = operations.front();
|
||||
|
||||
REQUIRE(log.entry_type == OperationLog::FunctionCall::TYPE_NAME);
|
||||
|
||||
const OperationLog::FunctionCall* call = (OperationLog::FunctionCall*)log.entry.get();
|
||||
|
||||
REQUIRE(call->function_name == "b");
|
||||
REQUIRE(call->call_result_type == "int");
|
||||
}
|
||||
|
||||
TEST_CASE("Find stacked function calls.", "[functions]")
|
||||
{
|
||||
RunOnCodeFixture fixture;
|
||||
|
||||
const std::string code = "int a(int); int b(); int main() { a(b()); }";
|
||||
const auto& operations = fixture(code);
|
||||
|
||||
REQUIRE(operations.size() == 2);
|
||||
|
||||
const OperationLog& log = operations.front();
|
||||
|
||||
REQUIRE(log.entry_type == OperationLog::FunctionCall::TYPE_NAME);
|
||||
|
||||
const OperationLog::FunctionCall* call = (OperationLog::FunctionCall*)log.entry.get();
|
||||
|
||||
REQUIRE(call->function_name == "a");
|
||||
REQUIRE(call->call_result_type == "int");
|
||||
|
||||
|
||||
const OperationLog& log_two = operations.back();
|
||||
|
||||
REQUIRE(log_two.entry_type == OperationLog::FunctionCall::TYPE_NAME);
|
||||
|
||||
const OperationLog::FunctionCall* call_two = (OperationLog::FunctionCall*)log_two.entry.get();
|
||||
|
||||
REQUIRE(call_two->function_name == "b");
|
||||
REQUIRE(call_two->call_result_type == "int");
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
//
|
||||
// Created by erki on 07.03.21.
|
||||
//
|
||||
|
||||
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
|
||||
#include <catch2/catch.hpp>
|
||||
@ -1,4 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
|
||||
find_package(Clang REQUIRED)
|
||||
find_package(LLVM REQUIRED COMPONENTS Support Option Core)
|
||||
|
||||
# LLVM is typically compiled without RTTI. Weird linker errors ensue if
|
||||
# you keep RTTI on and try to link.
|
||||
@ -12,9 +15,29 @@ if (NOT LLVM_ENABLE_RTTI)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
llvm_map_components_to_libnames(llvm_libs support option core)
|
||||
|
||||
add_executable(op-finder
|
||||
main.cpp)
|
||||
main.cpp
|
||||
OperationFinder.cpp
|
||||
OperationFinderConsumer.cpp
|
||||
OperationFinderAction.cpp
|
||||
OperationAnalyzer.cpp
|
||||
)
|
||||
|
||||
target_include_directories(op-finder
|
||||
PRIVATE ${LLVM_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_definitions(op-finder
|
||||
PRIVATE
|
||||
${LLVM_DEFINITIONS}
|
||||
)
|
||||
|
||||
target_link_libraries(op-finder
|
||||
PUBLIC
|
||||
op-finder-lib)
|
||||
PRIVATE
|
||||
${llvm_libs}
|
||||
clangTooling
|
||||
clangBasic
|
||||
clangASTMatchers
|
||||
)
|
||||
|
||||
135
op-finder/OperationAnalyzer.cpp
Normal file
135
op-finder/OperationAnalyzer.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
//
|
||||
// Created by erki on 24.02.21.
|
||||
//
|
||||
|
||||
#include "OperationAnalyzer.hpp"
|
||||
|
||||
#include <clang/Basic/SourceManager.h>
|
||||
|
||||
using namespace clang;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool IsInMainFile(const SourceManager& source_manager, const SourceLocation& loc)
|
||||
{
|
||||
//return source_manager.isWrittenInMainFile(loc);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename TOp>
|
||||
StringRef getOpcode(const TOp *op)
|
||||
{
|
||||
return op->getOpcodeStr(op->getOpcode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OperationAnalyzer::processAssignment(const BinaryOperator* op, const SourceManager& source_manager)
|
||||
{
|
||||
if (!IsInMainFile(source_manager, op->getBeginLoc()))
|
||||
return;
|
||||
|
||||
const auto& loc = op->getBeginLoc();
|
||||
|
||||
|
||||
llvm::outs() << source_manager.getFilename(loc) << ":"
|
||||
<< source_manager.getSpellingLineNumber(loc) << ":"
|
||||
<< source_manager.getSpellingColumnNumber(loc) << ":"
|
||||
<< "Assignment: Assigned to: ";
|
||||
|
||||
_processExpressionTypes(op, op->getLHS(), op->getRHS());
|
||||
|
||||
llvm::outs() << "\n";
|
||||
}
|
||||
|
||||
void OperationAnalyzer::processBinaryArithmetic(const BinaryOperator* op, const SourceManager& source_manager)
|
||||
{
|
||||
if (!IsInMainFile(source_manager, op->getBeginLoc()))
|
||||
return;
|
||||
|
||||
const auto& loc = op->getBeginLoc();
|
||||
|
||||
llvm::outs() << source_manager.getFilename(loc) << ":"
|
||||
<< source_manager.getSpellingLineNumber(loc) << ":"
|
||||
<< source_manager.getSpellingColumnNumber(loc) << ":"
|
||||
<< "Arithmetic: Type: " << getOpcode(op) << " LHS: ";
|
||||
|
||||
_processExpressionTypes(op, op->getLHS(), op->getRHS());
|
||||
|
||||
llvm::outs() << "\n";
|
||||
}
|
||||
|
||||
void OperationAnalyzer::processCompoundAssignment(const clang::CompoundAssignOperator* op, const SourceManager& source_manager)
|
||||
{
|
||||
if (!IsInMainFile(source_manager, op->getBeginLoc()))
|
||||
return;
|
||||
|
||||
const auto& loc = op->getBeginLoc();
|
||||
|
||||
llvm::outs() << source_manager.getFilename(loc) << ":"
|
||||
<< source_manager.getSpellingLineNumber(loc) << ":"
|
||||
<< source_manager.getSpellingColumnNumber(loc) << ":"
|
||||
<< "Compound assignment: Op code: " << getOpcode(op) << " ";
|
||||
|
||||
_processExpressionTypes(op, op->getLHS(), op->getRHS());
|
||||
|
||||
llvm::outs() << "\n";
|
||||
}
|
||||
|
||||
void OperationAnalyzer::processUnaryArithmetic(const UnaryOperator* op, const SourceManager& source_manager)
|
||||
{
|
||||
const Expr* lhs = op->getExprStmt();
|
||||
|
||||
if (!IsInMainFile(source_manager, lhs->getBeginLoc()))
|
||||
return;
|
||||
|
||||
const auto& loc = lhs->getBeginLoc();
|
||||
|
||||
llvm::outs() << source_manager.getFilename(loc) << ":"
|
||||
<< source_manager.getSpellingLineNumber(loc) << ":"
|
||||
<< source_manager.getSpellingColumnNumber(loc) << ":"
|
||||
<< "Unary arithmetic: Type: " << getOpcode(op) << " LHS: ";
|
||||
|
||||
_processExpressionTypes(op, lhs, nullptr);
|
||||
|
||||
llvm::outs() << "\n";
|
||||
}
|
||||
|
||||
void OperationAnalyzer::_processExpressionTypes(const Expr* source, const Expr* lhs,
|
||||
const Expr* rhs)
|
||||
{
|
||||
auto printTypeName = [](const QualType& t) -> void
|
||||
{
|
||||
if (t->isTypedefNameType())
|
||||
{
|
||||
const TypedefType* tdt = cast<TypedefType>(t);
|
||||
assert(tdt);
|
||||
llvm::outs() << "Typedef type: " << t.getAsString()
|
||||
<< ", underlying: " << tdt->desugar().getAsString()
|
||||
<< ".";
|
||||
}
|
||||
else if (t->isBuiltinType())
|
||||
{
|
||||
llvm::outs() << "Builtin type: " << t.getAsString() << ".";
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm::outs() << "Other type: " << t.getAsString() << ".";
|
||||
}
|
||||
};
|
||||
|
||||
llvm::outs() << "Expression types:\n\tExpression eval type: ";
|
||||
printTypeName(source->getType());
|
||||
|
||||
if (lhs)
|
||||
{
|
||||
llvm::outs() << "\n\tLHS eval type: ";
|
||||
printTypeName(lhs->getType());
|
||||
}
|
||||
if (rhs)
|
||||
{
|
||||
llvm::outs() << "\n\tRHS eval type: ";
|
||||
printTypeName(rhs->getType());
|
||||
}
|
||||
}
|
||||
23
op-finder/OperationAnalyzer.hpp
Normal file
23
op-finder/OperationAnalyzer.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// Created by erki on 24.02.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_OPERATIONANALYZER_HPP
|
||||
#define C_ANALYZER_OPERATIONANALYZER_HPP
|
||||
|
||||
#include <clang/AST/AST.h>
|
||||
|
||||
class OperationAnalyzer
|
||||
{
|
||||
public:
|
||||
void processAssignment(const clang::BinaryOperator* op, const clang::SourceManager& source_manager);
|
||||
void processBinaryArithmetic(const clang::BinaryOperator* op, const clang::SourceManager& source_manager);
|
||||
void processCompoundAssignment(const clang::CompoundAssignOperator* op, const clang::SourceManager& source_manager);
|
||||
void processUnaryArithmetic(const clang::UnaryOperator* op, const clang::SourceManager& source_manager);
|
||||
|
||||
private:
|
||||
void _processExpressionTypes(const clang::Expr* source, const clang::Expr* lhs, const clang::Expr* rhs);
|
||||
};
|
||||
|
||||
|
||||
#endif //C_ANALYZER_OPERATIONANALYZER_HPP
|
||||
114
op-finder/OperationFinder.cpp
Normal file
114
op-finder/OperationFinder.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
//
|
||||
// Created by erki on 16.02.21.
|
||||
//
|
||||
|
||||
#include "OperationFinder.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
StatementMatcher AssignmentMatcher =
|
||||
binaryOperation(isAssignmentOperator(),
|
||||
hasLHS(expr().bind("lhs")),
|
||||
hasRHS(expr().bind("rhs"))).bind("assignment");
|
||||
|
||||
StatementMatcher ArithmeticMatcher =
|
||||
binaryOperation(hasAnyOperatorName("+", "-", "/", "*", "<", ">",
|
||||
"<=", ">=", "==", "<<", ">>", "%"),
|
||||
hasLHS(expr().bind("lhs")),
|
||||
hasRHS(expr().bind("lhs"))).bind("arithmetic");
|
||||
|
||||
StatementMatcher UnaryArithmeticMatcher =
|
||||
unaryOperator(hasAnyOperatorName("!", "++", "--"),
|
||||
hasUnaryOperand(expr().bind("lhs"))).bind("unary_arithmetic");
|
||||
|
||||
StatementMatcher CompoundMatcher =
|
||||
compoundStmt().bind("compound_stmt");
|
||||
|
||||
bool isInMainFile(const MatchFinder::MatchResult &result, const SourceLocation &loc)
|
||||
{
|
||||
return result.Context->getSourceManager().isWrittenInMainFile(loc);
|
||||
}
|
||||
|
||||
template<typename TOp>
|
||||
StringRef getOpcode(const TOp *op)
|
||||
{
|
||||
return op->getOpcodeStr(op->getOpcode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OperationFinder::addMatcher(MatchFinder &finder)
|
||||
{
|
||||
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, ArithmeticMatcher), this);
|
||||
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, AssignmentMatcher), this);
|
||||
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, CompoundMatcher), this);
|
||||
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, UnaryArithmeticMatcher), this);
|
||||
}
|
||||
|
||||
void OperationFinder::run(const MatchFinder::MatchResult &result)
|
||||
{
|
||||
if (result.Nodes.getNodeAs<clang::BinaryOperator>("assignment"))
|
||||
{
|
||||
_processAssignment(result);
|
||||
}
|
||||
else if (result.Nodes.getNodeAs<clang::BinaryOperator>("arithmetic"))
|
||||
{
|
||||
_processArithmetic(result);
|
||||
}
|
||||
else if (result.Nodes.getNodeAs<clang::CompoundStmt>("compound_stmt"))
|
||||
{
|
||||
_processCompoundStmt(result);
|
||||
}
|
||||
else if (result.Nodes.getNodeAs<clang::UnaryOperator>("unary_arithmetic"))
|
||||
{
|
||||
_processUnaryArithmetic(result);
|
||||
}
|
||||
}
|
||||
|
||||
void OperationFinder::_processAssignment(const clang::ast_matchers::MatchFinder::MatchResult& result)
|
||||
{
|
||||
const BinaryOperator* op = result.Nodes.getNodeAs<BinaryOperator>("assignment");
|
||||
|
||||
assert(op);
|
||||
|
||||
_operation_analyzer.processAssignment(op, *result.SourceManager);
|
||||
}
|
||||
|
||||
void OperationFinder::_processArithmetic(const MatchFinder::MatchResult& result)
|
||||
{
|
||||
const BinaryOperator* op = result.Nodes.getNodeAs<clang::BinaryOperator>("arithmetic");
|
||||
|
||||
assert(op);
|
||||
|
||||
_operation_analyzer.processBinaryArithmetic(op, *result.SourceManager);
|
||||
}
|
||||
|
||||
void OperationFinder::_processCompoundStmt(const clang::ast_matchers::MatchFinder::MatchResult& result)
|
||||
{
|
||||
const CompoundStmt* stmt = result.Nodes.getNodeAs<CompoundStmt>("compound_stmt");
|
||||
|
||||
assert(stmt);
|
||||
|
||||
const CompoundAssignOperator* op = dyn_cast<CompoundAssignOperator>(stmt->getStmtExprResult());
|
||||
|
||||
if (!op || !op->isCompoundAssignmentOp())
|
||||
return;
|
||||
|
||||
_operation_analyzer.processCompoundAssignment(op, *result.SourceManager);
|
||||
}
|
||||
|
||||
void OperationFinder::_processUnaryArithmetic(const MatchFinder::MatchResult &result)
|
||||
{
|
||||
const UnaryOperator* op = result.Nodes.getNodeAs<UnaryOperator>("unary_arithmetic");
|
||||
|
||||
assert(op);
|
||||
|
||||
_operation_analyzer.processUnaryArithmetic(op, *result.SourceManager);
|
||||
}
|
||||
28
op-finder/OperationFinder.hpp
Normal file
28
op-finder/OperationFinder.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// Created by erki on 16.02.21.
|
||||
//
|
||||
|
||||
#ifndef LLVM_PROTO_OPERATIONFINDER_HPP
|
||||
#define LLVM_PROTO_OPERATIONFINDER_HPP
|
||||
|
||||
#include <clang/ASTMatchers/ASTMatchers.h>
|
||||
#include <clang/ASTMatchers/ASTMatchFinder.h>
|
||||
|
||||
#include "OperationAnalyzer.hpp"
|
||||
|
||||
class OperationFinder : public clang::ast_matchers::MatchFinder::MatchCallback
|
||||
{
|
||||
public:
|
||||
void addMatcher(clang::ast_matchers::MatchFinder& finder);
|
||||
|
||||
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult& result) override;
|
||||
private:
|
||||
void _processAssignment(const clang::ast_matchers::MatchFinder::MatchResult& result);
|
||||
void _processArithmetic(const clang::ast_matchers::MatchFinder::MatchResult& result);
|
||||
void _processCompoundStmt(const clang::ast_matchers::MatchFinder::MatchResult& result);
|
||||
void _processUnaryArithmetic(const clang::ast_matchers::MatchFinder::MatchResult& result);
|
||||
|
||||
OperationAnalyzer _operation_analyzer;
|
||||
};
|
||||
|
||||
#endif //LLVM_PROTO_OPERATIONFINDER_HPP
|
||||
5
op-finder/OperationFinderAction.cpp
Normal file
5
op-finder/OperationFinderAction.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by erki on 24.02.21.
|
||||
//
|
||||
|
||||
#include "OperationFinderAction.hpp"
|
||||
23
op-finder/OperationFinderAction.hpp
Normal file
23
op-finder/OperationFinderAction.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// Created by erki on 24.02.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_OPERATIONFINDERACTION_HPP
|
||||
#define C_ANALYZER_OPERATIONFINDERACTION_HPP
|
||||
|
||||
#include <clang/AST/RecursiveASTVisitor.h>
|
||||
|
||||
class OperationFinderAction
|
||||
: public clang::RecursiveASTVisitor<OperationFinderAction>
|
||||
{
|
||||
public:
|
||||
bool VisitBinaryOperation(clang::BinaryOperator* bo);
|
||||
bool VisitUnaryOperation(clang::UnaryOperator* uo);
|
||||
bool VisitCompoundStatement(clang::CompoundStmt* cmp);
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //C_ANALYZER_OPERATIONFINDERACTION_HPP
|
||||
25
op-finder/OperationFinderConsumer.cpp
Normal file
25
op-finder/OperationFinderConsumer.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by erki on 24.02.21.
|
||||
//
|
||||
|
||||
#include "OperationFinderConsumer.hpp"
|
||||
|
||||
OperationFinderConsumer::OperationFinderConsumer()
|
||||
{
|
||||
_op_finder.addMatcher(_ast_finder);
|
||||
}
|
||||
|
||||
void OperationFinderConsumer::HandleTranslationUnit(clang::ASTContext& context)
|
||||
{
|
||||
//_ast_finder.matchAST(context);
|
||||
auto consumer = _ast_finder.newASTConsumer();
|
||||
|
||||
consumer->HandleTranslationUnit(context);
|
||||
}
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer> OperationFinderAction::CreateASTConsumer(
|
||||
clang::CompilerInstance& compiler, llvm::StringRef in_file)
|
||||
{
|
||||
return std::unique_ptr<clang::ASTConsumer>(
|
||||
new OperationFinderConsumer());
|
||||
}
|
||||
32
op-finder/OperationFinderConsumer.hpp
Normal file
32
op-finder/OperationFinderConsumer.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// Created by erki on 24.02.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_OPERATIONFINDERCONSUMER_HPP
|
||||
#define C_ANALYZER_OPERATIONFINDERCONSUMER_HPP
|
||||
|
||||
#include <clang/AST/ASTConsumer.h>
|
||||
#include <clang/Frontend/FrontendAction.h>
|
||||
|
||||
#include "OperationFinder.hpp"
|
||||
|
||||
class OperationFinderConsumer : public clang::ASTConsumer
|
||||
{
|
||||
public:
|
||||
OperationFinderConsumer();
|
||||
|
||||
void HandleTranslationUnit(clang::ASTContext& context) override;
|
||||
|
||||
private:
|
||||
OperationFinder _op_finder;
|
||||
clang::ast_matchers::MatchFinder _ast_finder;
|
||||
};
|
||||
|
||||
class OperationFinderAction : public clang::ASTFrontendAction
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
|
||||
clang::CompilerInstance& compiler, llvm::StringRef in_file) override;
|
||||
};
|
||||
|
||||
#endif //C_ANALYZER_OPERATIONFINDERCONSUMER_HPP
|
||||
62
op-finder/TestASTConsumer.hpp
Normal file
62
op-finder/TestASTConsumer.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
//
|
||||
// Created by erki on 24.02.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_TESTASTCONSUMER_HPP
|
||||
#define C_ANALYZER_TESTASTCONSUMER_HPP
|
||||
|
||||
#include <clang/Frontend/CompilerInstance.h>
|
||||
#include <clang/Frontend/FrontendActions.h>
|
||||
#include <clang/AST/RecursiveASTVisitor.h>
|
||||
|
||||
class DebuggeringASTVisitor
|
||||
: public clang::RecursiveASTVisitor<DebuggeringASTVisitor>
|
||||
{
|
||||
public:
|
||||
explicit DebuggeringASTVisitor(clang::ASTContext* context)
|
||||
: _context(context)
|
||||
{ }
|
||||
|
||||
bool VisitBinaryOperator(clang::BinaryOperator* bo)
|
||||
{
|
||||
llvm::outs() << "OKAY FUCK YOU\n";
|
||||
bo->dump();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
clang::ASTContext* _context;
|
||||
};
|
||||
|
||||
class DebuggeringASTConsumer : public clang::ASTConsumer
|
||||
{
|
||||
public:
|
||||
explicit DebuggeringASTConsumer(clang::ASTContext* context)
|
||||
: _visitor(context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HandleTranslationUnit(clang::ASTContext& context) override
|
||||
{
|
||||
_visitor.TraverseAST(context);
|
||||
}
|
||||
private:
|
||||
DebuggeringASTVisitor _visitor;
|
||||
};
|
||||
|
||||
class DebuggeringASTAction : public clang::ASTFrontendAction
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
|
||||
clang::CompilerInstance &Compiler, llvm::StringRef InFile) override
|
||||
{
|
||||
return std::unique_ptr<clang::ASTConsumer>(
|
||||
new DebuggeringASTConsumer(&Compiler.getASTContext()));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //C_ANALYZER_TESTASTCONSUMER_HPP
|
||||
@ -5,12 +5,9 @@
|
||||
// Declares llvm::cl::extrahelp.
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "OperationAstMatcher.hpp"
|
||||
#include "OperationFinder.hpp"
|
||||
#include "OperationStorage.hpp"
|
||||
#include "OperationFinderAstAction.hpp"
|
||||
#include "TestASTConsumer.hpp"
|
||||
#include "OperationFinderConsumer.hpp"
|
||||
|
||||
using namespace clang::tooling;
|
||||
using namespace clang::ast_matchers;
|
||||
@ -18,15 +15,7 @@ using namespace llvm;
|
||||
|
||||
// Apply a custom category to all command-line options so that they are the
|
||||
// only ones displayed.
|
||||
static cl::OptionCategory MyToolCategory("op-finder options");
|
||||
|
||||
static cl::opt<std::string> OutputFile("o", cl::desc("File to output the JSON to."),
|
||||
cl::cat(MyToolCategory));
|
||||
|
||||
static cl::opt<std::string> RootDirectory("r", cl::desc("The root directory of the source files."),
|
||||
cl::cat(MyToolCategory));
|
||||
|
||||
static cl::opt<bool> PrettyPrint("pretty", cl::desc("Pretty-print the output JSON."));
|
||||
static llvm::cl::OptionCategory MyToolCategory("opcounter options");
|
||||
|
||||
// CommonOptionsParser declares HelpMessage with a description of the common
|
||||
// command-line options related to the compilation database and input files.
|
||||
@ -34,9 +23,7 @@ static cl::opt<bool> PrettyPrint("pretty", cl::desc("Pretty-print the output JSO
|
||||
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
|
||||
|
||||
// A help message for this specific tool can be added afterwards.
|
||||
static cl::extrahelp MoreHelp("\nThe program takes the input <source0> ... files, parses their\n"
|
||||
"AST and outputs a singular file containing a list of all noteworthy operations\n"
|
||||
"for later analysis.\n");
|
||||
static cl::extrahelp MoreHelp("\nMore help text...\n");
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
@ -52,30 +39,14 @@ int main(int argc, const char** argv)
|
||||
ClangTool Tool(OptionsParser.getCompilations(),
|
||||
OptionsParser.getSourcePathList());
|
||||
|
||||
OperationStorage storage;
|
||||
OperationFinder op_finder;
|
||||
MatchFinder finder;
|
||||
|
||||
if (PrettyPrint.getValue())
|
||||
storage.enablePrettyPrint();
|
||||
op_finder.addMatcher(finder);
|
||||
|
||||
OperationFinder op_finder(&storage);
|
||||
// Tool.run(newFrontendActionFactory<clang::ASTPrintAction>().get());
|
||||
Tool.run(newFrontendActionFactory<DebuggeringASTAction>().get());
|
||||
Tool.run(newFrontendActionFactory<OperationFinderAction>().get());
|
||||
|
||||
#if 0
|
||||
MatchFinder matcher;
|
||||
OperationASTMatcher finder(&op_finder);
|
||||
|
||||
finder.addToFinder(matcher);
|
||||
|
||||
//Tool.run(newFrontendActionFactory<DebuggeringASTAction>().get());
|
||||
|
||||
return Tool.run(newFrontendActionFactory(&finder).get());
|
||||
#endif
|
||||
|
||||
OperationFinderAstAction action(&op_finder);
|
||||
|
||||
Tool.run(newFrontendActionFactory(&action).get());
|
||||
|
||||
if (!OutputFile.getValue().empty())
|
||||
storage.toFile(OutputFile.getValue());
|
||||
else
|
||||
storage.toStream(std::cout);
|
||||
// return Tool.run(newFrontendActionFactory(&finder).get());
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -1,213 +0,0 @@
|
||||
{
|
||||
"matrix.c": [
|
||||
{
|
||||
"branch_number": 1,
|
||||
"entry": {
|
||||
"operation_name": "=",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 20
|
||||
},
|
||||
{
|
||||
"branch_number": 2,
|
||||
"entry": {
|
||||
"operation_name": "<",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 20
|
||||
},
|
||||
{
|
||||
"branch_number": 2,
|
||||
"entry": {
|
||||
"operation_name": "++",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": ""
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 20
|
||||
},
|
||||
{
|
||||
"branch_number": 1,
|
||||
"entry": {
|
||||
"operation_name": "=",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 22
|
||||
},
|
||||
{
|
||||
"branch_number": 2,
|
||||
"entry": {
|
||||
"operation_name": "<",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 22
|
||||
},
|
||||
{
|
||||
"branch_number": 2,
|
||||
"entry": {
|
||||
"operation_name": "++",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": ""
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 22
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "=",
|
||||
"type_lhs": "unsigned short",
|
||||
"type_result": "unsigned short",
|
||||
"type_rhs": "unsigned short"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 24
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "subscript",
|
||||
"type_lhs": "volatile UInt16 *",
|
||||
"type_result": "unsigned short",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 24
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "subscript",
|
||||
"type_lhs": "volatile UInt16 (*)[5]",
|
||||
"type_result": "volatile UInt16 [5]",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 24
|
||||
},
|
||||
{
|
||||
"branch_number": 1,
|
||||
"entry": {
|
||||
"operation_name": "=",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 25
|
||||
},
|
||||
{
|
||||
"branch_number": 2,
|
||||
"entry": {
|
||||
"operation_name": "<",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 25
|
||||
},
|
||||
{
|
||||
"branch_number": 2,
|
||||
"entry": {
|
||||
"operation_name": "++",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": ""
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 25
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "subscript",
|
||||
"type_lhs": "volatile UInt16 *",
|
||||
"type_result": "unsigned short",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 27
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "subscript",
|
||||
"type_lhs": "volatile UInt16 (*)[5]",
|
||||
"type_result": "volatile UInt16 [5]",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 27
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "*",
|
||||
"type_lhs": "int",
|
||||
"type_result": "int",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 27
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "subscript",
|
||||
"type_lhs": "const UInt16 *",
|
||||
"type_result": "unsigned short",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 27
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "subscript",
|
||||
"type_lhs": "const UInt16 (*)[4]",
|
||||
"type_result": "const UInt16 [4]",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 27
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "subscript",
|
||||
"type_lhs": "const UInt16 *",
|
||||
"type_result": "unsigned short",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 27
|
||||
},
|
||||
{
|
||||
"branch_number": 0,
|
||||
"entry": {
|
||||
"operation_name": "subscript",
|
||||
"type_lhs": "const UInt16 (*)[5]",
|
||||
"type_result": "const UInt16 [5]",
|
||||
"type_rhs": "int"
|
||||
},
|
||||
"entry_type": "basic_operation",
|
||||
"line": 27
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
import json
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
@dataclass
|
||||
class GCovLine:
|
||||
line_number: int
|
||||
count: int
|
||||
branch_number: int = 0
|
||||
|
||||
|
||||
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"]:
|
||||
# Branch specific identification. TODO! later.
|
||||
lines.append(GCovLine(line["line_number"], line["count"]))
|
||||
if len(line["branches"]):
|
||||
# GCov reports branches in reverse order to our parser.
|
||||
branch_number = len(line["branches"])
|
||||
for branch in line["branches"]:
|
||||
lines.append(GCovLine(line["line_number"], branch["count"], branch_number))
|
||||
branch_number -= 1
|
||||
|
||||
self.files[name] = lines
|
||||
@ -1,73 +0,0 @@
|
||||
import json
|
||||
|
||||
from enum import Enum
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
|
||||
class OperationType(Enum):
|
||||
BASIC = "basic_operation"
|
||||
FUNCTION_CALL = "function_call"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BasicOperation:
|
||||
operation_name: str
|
||||
type_lhs: str
|
||||
type_rhs: str
|
||||
type_result: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FunctionCall:
|
||||
function_name: str
|
||||
call_result_type: str
|
||||
|
||||
|
||||
UniqueOperation = Union[BasicOperation, FunctionCall]
|
||||
|
||||
|
||||
@dataclass
|
||||
class OperationLog:
|
||||
line: int
|
||||
branch_number: int
|
||||
entry_type: OperationType
|
||||
entry: UniqueOperation
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.entry_type = OperationType(self.entry_type)
|
||||
|
||||
if self.entry_type == OperationType.BASIC:
|
||||
self.entry = BasicOperation(**self.entry)
|
||||
elif self.entry_type == OperationType.FUNCTION_CALL:
|
||||
self.entry = FunctionCall(**self.entry)
|
||||
else:
|
||||
assert False, "Unaccounted for operation type."
|
||||
|
||||
|
||||
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
|
||||
@ -1,140 +0,0 @@
|
||||
import json
|
||||
|
||||
from dataclasses import asdict
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
from gcovreader import GCovFile
|
||||
from opfinderreader import OperationLogReader, UniqueOperation, OperationLog
|
||||
|
||||
|
||||
class OpSummarizer:
|
||||
def __init__(self, gcov_path: str, opfinder_path: str) -> None:
|
||||
self.gcov = GCovFile(gcov_path)
|
||||
self.ops = OperationLogReader(opfinder_path)
|
||||
|
||||
self.gcov.read()
|
||||
self.ops.read()
|
||||
|
||||
self.model: Dict[UniqueOperation, Tuple[float, float]] = None
|
||||
self.uncounted_estimates: Dict[UniqueOperation, int] = {}
|
||||
self.energy_consumption_estimation = 0.0
|
||||
self.execution_time_estimation = 0.0
|
||||
|
||||
def add_model(self, model_path: str) -> None:
|
||||
with open(model_path, "r") as model_file:
|
||||
data = json.load(model_file)
|
||||
|
||||
assert type(data) == list, "Model file did not contain a JSON list."
|
||||
|
||||
self.model = {}
|
||||
for entry in data:
|
||||
unique_op = OperationLog(**entry["unique_operation"]).entry
|
||||
energy = entry["energy_consumption"]
|
||||
time = entry["execution_time"]
|
||||
|
||||
self.model[unique_op] = [energy, time]
|
||||
|
||||
def count_operations(self, file: str) -> Dict[UniqueOperation, int]:
|
||||
if file not in self.gcov.files or file not in self.ops.files:
|
||||
print(f"Gcov files: {self.gcov.files.keys()}")
|
||||
print(f"Opfinder files: {self.ops.files.keys()}")
|
||||
raise RuntimeError(f"File {file} not in both parsers.")
|
||||
|
||||
op_counter: Dict[UniqueOperation, int] = {}
|
||||
|
||||
for gcov_line in self.gcov.files[file]:
|
||||
op_lines = self.ops.get_lines(file, gcov_line.line_number)
|
||||
for op_log in op_lines:
|
||||
# TODO: revise this. Need a special case for for-loop clauses
|
||||
# or branching in general.
|
||||
if op_log.branch_number != gcov_line.branch_number:
|
||||
continue
|
||||
|
||||
unique_op = op_log.entry
|
||||
|
||||
if unique_op in op_counter:
|
||||
op_counter[unique_op] += gcov_line.count
|
||||
else:
|
||||
op_counter[unique_op] = gcov_line.count
|
||||
|
||||
return op_counter
|
||||
|
||||
def update_estimations(self, uops: Dict[UniqueOperation, int]) -> None:
|
||||
assert self.model, "Model not populated."
|
||||
for unique_op, op_count in uops.items():
|
||||
if unique_op in self.model:
|
||||
energy, time = self.model[unique_op]
|
||||
self.energy_consumption_estimation += energy * op_count
|
||||
self.execution_time_estimation += time * op_count
|
||||
elif unique_op in self.uncounted_estimates:
|
||||
self.uncounted_estimates[unique_op] += op_count
|
||||
else:
|
||||
self.uncounted_estimates[unique_op] = op_count
|
||||
|
||||
@staticmethod
|
||||
def operation_count_to_json_dict(unique_ops: Dict[UniqueOperation, int]) -> List[Dict]:
|
||||
out = []
|
||||
|
||||
for uo, uo_count in unique_ops.items():
|
||||
d = asdict(uo)
|
||||
d["count"] = uo_count
|
||||
out.append(d)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Merges gcovr and op-finder outputs.")
|
||||
parser.add_argument("files", metavar="FILES", type=str, nargs="+",
|
||||
help="The files to accumulate.")
|
||||
parser.add_argument("--gcov", type=str, default="./data/gcov.json",
|
||||
help="The gcovr json file to use.")
|
||||
parser.add_argument("--finder", type=str, default="./data/opfinder.json",
|
||||
help="The op-finder json file to use.")
|
||||
parser.add_argument("--output", type=str, default=None, required=False,
|
||||
help="The file to output the data to.")
|
||||
parser.add_argument("--model", type=str, default=None, required=False,
|
||||
help="The JSON file containing the system model.")
|
||||
args = parser.parse_args()
|
||||
|
||||
summarizer = OpSummarizer(args.gcov, args.finder)
|
||||
|
||||
if args.model:
|
||||
summarizer.add_model(args.model)
|
||||
|
||||
uops_dictionary = {}
|
||||
total_num = 0
|
||||
|
||||
for file_name in args.files:
|
||||
ops = summarizer.count_operations(file_name)
|
||||
uops_dictionary[file_name] = summarizer.operation_count_to_json_dict(ops)
|
||||
|
||||
if args.model:
|
||||
summarizer.update_estimations(ops)
|
||||
|
||||
print(f"Unique operations for file {file_name}:")
|
||||
for uop, count in ops.items():
|
||||
print(f"\t{count}: {uop}")
|
||||
total_num += count
|
||||
|
||||
print("---------")
|
||||
|
||||
print(f"Total count: {total_num}")
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w") as outfile:
|
||||
json.dump(uops_dictionary, outfile)
|
||||
|
||||
if args.model:
|
||||
power_usage_estimate = summarizer.energy_consumption_estimation
|
||||
time_estimate = summarizer.execution_time_estimation
|
||||
print("---------")
|
||||
print(f"Total energy usage estimation: {power_usage_estimate} mJ\nTotal execution time estimation: {time_estimate} ms")
|
||||
if summarizer.uncounted_estimates:
|
||||
print("Operations not accounted for (missing from the model):")
|
||||
for uop, count in summarizer.uncounted_estimates.items():
|
||||
print(f"\t{uop}: {count}")
|
||||
else:
|
||||
print("No operations missing from the model.")
|
||||
@ -1,76 +0,0 @@
|
||||
import unittest
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
from dataclasses import asdict
|
||||
from typing import Dict
|
||||
|
||||
from opfinderreader import UniqueOperation
|
||||
from opsummarizer import OpSummarizer
|
||||
|
||||
|
||||
class Compiler:
|
||||
def __init__(self, root_file: str) -> None:
|
||||
self.root_file = root_file
|
||||
self.gcov_file = f"./{root_file}_gcov.json"
|
||||
self.opfinder_file = f"./{root_file}_opfinder.json"
|
||||
|
||||
def compile_and_profile(self) -> None:
|
||||
output_file = f"{self.root_file}.out"
|
||||
input_file = f"{self.root_file}.c"
|
||||
|
||||
subprocess.call(["gcc",
|
||||
"-fprofile-arcs",
|
||||
"-ftest-coverage",
|
||||
"-o", output_file,
|
||||
input_file])
|
||||
|
||||
subprocess.call([f"./{output_file}"])
|
||||
|
||||
subprocess.call(["gcovr",
|
||||
"-r", ".",
|
||||
"--json",
|
||||
"--output", self.gcov_file])
|
||||
|
||||
def find_operations(self) -> None:
|
||||
input_file = f"{self.root_file}.c"
|
||||
|
||||
subprocess.call(["./op-finder",
|
||||
"-o", self.opfinder_file,
|
||||
input_file])
|
||||
|
||||
|
||||
class SummarizerCreatesExpectedOutput(unittest.TestCase):
|
||||
def __init__(self, test_name, file_name) -> None:
|
||||
super(SummarizerCreatesExpectedOutput, self).__init__(test_name)
|
||||
self.root_file_name = file_name
|
||||
self.compiler = Compiler(file_name)
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.compiler.compile_and_profile()
|
||||
self.compiler.find_operations()
|
||||
|
||||
def _get_etalon(self) -> Dict:
|
||||
filename = f"{self.root_file_name}_expected.json"
|
||||
with open(filename, "r") as f:
|
||||
return json.load(f)[f"{self.root_file_name}.c"]
|
||||
|
||||
def test_summarizer_output(self) -> None:
|
||||
summarizer = OpSummarizer(self.compiler.gcov_file, self.compiler.opfinder_file)
|
||||
found = summarizer.count_operations(f"{self.root_file_name}.c")
|
||||
found = summarizer.operation_count_to_json_dict(found)
|
||||
|
||||
etalon = self._get_etalon()
|
||||
|
||||
self.assertEqual(found, etalon, msg="Found operations doesn't match etalon dictionary.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
suite.addTest(SummarizerCreatesExpectedOutput("test_summarizer_output", "matrix"))
|
||||
# suite.addTest(SummarizerCreatesExpectedOutput("test_summarizer_output", "gauss_blur"))
|
||||
# suite.addTest(SummarizerCreatesExpectedOutput("test_summarizer_output", "for_loop"))
|
||||
# suite.addTest(SummarizerCreatesExpectedOutput("test_summarizer_output", "fir"))
|
||||
|
||||
unittest.TextTestRunner(verbosity=2).run(suite)
|
||||
@ -1,235 +0,0 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Name : Dhrystone
|
||||
* Purpose : Benchmark the Dhrystone code. This benchmark is used to gauge
|
||||
* the performance of the microcontroller in handling pointers,
|
||||
* structures and strings.
|
||||
*
|
||||
*******************************************************************************/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LOOPS 100 /* Use this for slow or 16 bit machines */
|
||||
#define structassign(d, s) d = s
|
||||
typedef enum {
|
||||
Ident1, Ident2, Ident3, Ident4, Ident5
|
||||
} Enumeration;
|
||||
typedef int OneToThirty;
|
||||
typedef int OneToFifty;
|
||||
typedef unsigned char CapitalLetter;
|
||||
typedef unsigned char String30[31];
|
||||
typedef int Array1Dim[51];
|
||||
typedef int Array2Dim[10][10];
|
||||
struct Record
|
||||
{
|
||||
struct Record *PtrComp;
|
||||
Enumeration Discr;
|
||||
Enumeration EnumComp;
|
||||
OneToFifty IntComp;
|
||||
String30 StringComp;
|
||||
};
|
||||
typedef struct Record RecordType;
|
||||
typedef RecordType * RecordPtr;
|
||||
typedef int boolean;
|
||||
//#define NULL 0
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define REG register
|
||||
int IntGlob;
|
||||
boolean BoolGlob;
|
||||
unsigned char Char1Glob;
|
||||
unsigned char Char2Glob;
|
||||
Array1Dim Array1Glob;
|
||||
Array2Dim Array2Glob;
|
||||
RecordPtr PtrGlb;
|
||||
RecordPtr PtrGlbNext;
|
||||
RecordType rec1, rec2;
|
||||
Enumeration Func1(CapitalLetter CharPar1, CapitalLetter CharPar2) {
|
||||
REG CapitalLetter CharLoc1;
|
||||
REG CapitalLetter CharLoc2;
|
||||
CharLoc1 = CharPar1;
|
||||
CharLoc2 = CharLoc1;
|
||||
if (CharLoc2 != CharPar2)
|
||||
return (Ident1);
|
||||
else
|
||||
return (Ident2);
|
||||
}
|
||||
boolean Func2(String30 StrParI1, String30 StrParI2) {
|
||||
REG OneToThirty IntLoc;
|
||||
REG CapitalLetter CharLoc;
|
||||
IntLoc = 1;
|
||||
while (IntLoc <= 1)
|
||||
if (Func1(StrParI1[IntLoc], StrParI2[IntLoc + 1]) == Ident1) {
|
||||
CharLoc = 'A';
|
||||
++IntLoc;
|
||||
}
|
||||
if (CharLoc >= 'W' && CharLoc <= 'Z')
|
||||
IntLoc = 7;
|
||||
if (CharLoc == 'X')
|
||||
return (TRUE);
|
||||
else {
|
||||
if (strcmp(StrParI1, StrParI2) > 0) {
|
||||
IntLoc += 7;
|
||||
return (TRUE);
|
||||
} else
|
||||
return (FALSE);
|
||||
}
|
||||
}
|
||||
boolean Func3(Enumeration EnumParIn) {
|
||||
REG Enumeration EnumLoc;
|
||||
EnumLoc = EnumParIn;
|
||||
if (EnumLoc == Ident3)
|
||||
return (TRUE);
|
||||
return (FALSE);
|
||||
}
|
||||
void Proc7(OneToFifty IntParI1, OneToFifty IntParI2, OneToFifty *IntParOut) {
|
||||
REG OneToFifty IntLoc;
|
||||
IntLoc = IntParI1 + 2;
|
||||
*IntParOut = IntParI2 + IntLoc;
|
||||
}
|
||||
void Proc4(void) {
|
||||
REG boolean BoolLoc;
|
||||
BoolLoc = Char1Glob == 'A';
|
||||
BoolLoc |= BoolGlob;
|
||||
Char2Glob = 'B';
|
||||
}
|
||||
void Proc5(void) {
|
||||
Char1Glob = 'A';
|
||||
BoolGlob = FALSE;
|
||||
}
|
||||
void Proc6(Enumeration EnumParIn, Enumeration *EnumParOut) {
|
||||
*EnumParOut = EnumParIn;
|
||||
if (!Func3(EnumParIn))
|
||||
*EnumParOut = Ident4;
|
||||
switch (EnumParIn) {
|
||||
case Ident1:
|
||||
*EnumParOut = Ident1;
|
||||
break;
|
||||
case Ident2:
|
||||
if (IntGlob > 100)
|
||||
*EnumParOut = Ident1;
|
||||
else
|
||||
*EnumParOut = Ident4;
|
||||
break;
|
||||
case Ident3:
|
||||
*EnumParOut = Ident2;
|
||||
break;
|
||||
case Ident4:
|
||||
break;
|
||||
case Ident5:
|
||||
*EnumParOut = Ident3;
|
||||
}
|
||||
}
|
||||
void Proc3(RecordPtr *PtrParOut) {
|
||||
if (PtrGlb != NULL)
|
||||
*PtrParOut = PtrGlb->PtrComp;
|
||||
else
|
||||
IntGlob = 100;
|
||||
Proc7(10, IntGlob, &PtrGlb->IntComp);
|
||||
}
|
||||
void Proc1(RecordPtr PtrParIn) {
|
||||
#define NextRecord (*(PtrParIn->PtrComp))
|
||||
structassign(NextRecord, *PtrGlb);
|
||||
PtrParIn->IntComp = 5;
|
||||
NextRecord.IntComp = PtrParIn->IntComp;
|
||||
NextRecord.PtrComp = PtrParIn->PtrComp;
|
||||
Proc3(&NextRecord.PtrComp);
|
||||
if (NextRecord.Discr == Ident1) {
|
||||
NextRecord.IntComp = 6;
|
||||
Proc6(PtrParIn->EnumComp, &NextRecord.EnumComp);
|
||||
NextRecord.PtrComp = PtrGlb->PtrComp;
|
||||
Proc7(NextRecord.IntComp, 10, &NextRecord.IntComp);
|
||||
} else
|
||||
structassign(*PtrParIn, NextRecord);
|
||||
#undef NextRecord
|
||||
}
|
||||
void Proc2(OneToFifty *IntParIO) {
|
||||
REG OneToFifty IntLoc;
|
||||
REG Enumeration EnumLoc;
|
||||
IntLoc = *IntParIO + 10;
|
||||
for (;;) {
|
||||
if (Char1Glob == 'A') {
|
||||
--IntLoc;
|
||||
*IntParIO = IntLoc - IntGlob;
|
||||
EnumLoc = Ident1;
|
||||
}
|
||||
if (EnumLoc == Ident1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Proc8(Array1Dim Array1Par, Array2Dim Array2Par, OneToFifty IntParI1,
|
||||
OneToFifty IntParI2) {
|
||||
REG OneToFifty IntLoc;
|
||||
REG OneToFifty IntIndex;
|
||||
IntLoc = IntParI1 + 5;
|
||||
Array1Par[IntLoc] = IntParI2;
|
||||
Array1Par[IntLoc + 1] = Array1Par[IntLoc];
|
||||
Array1Par[IntLoc + 30] = IntLoc;
|
||||
for (IntIndex = IntLoc; IntIndex <= (IntLoc + 1); ++IntIndex)
|
||||
Array2Par[IntLoc][IntIndex] = IntLoc;
|
||||
++Array2Par[IntLoc][IntLoc - 1];
|
||||
Array2Par[IntLoc + 20][IntLoc] = Array1Par[IntLoc];
|
||||
IntGlob = 5;
|
||||
}
|
||||
void Proc0(void) {
|
||||
OneToFifty IntLoc1;
|
||||
REG OneToFifty IntLoc2;
|
||||
OneToFifty IntLoc3;
|
||||
REG unsigned char CharLoc;
|
||||
REG unsigned char CharIndex;
|
||||
Enumeration EnumLoc;
|
||||
String30 String1Loc;
|
||||
String30 String2Loc;
|
||||
//extern unsigned char *malloc();
|
||||
long time(long *);
|
||||
long starttime;
|
||||
long benchtime;
|
||||
long nulltime;
|
||||
register unsigned int i;
|
||||
for (i = 0; i < LOOPS; ++i)
|
||||
;
|
||||
PtrGlbNext = &rec1; /* (RecordPtr) malloc(sizeof(RecordType)); */
|
||||
PtrGlb = &rec2; /* (RecordPtr) malloc(sizeof(RecordType)); */
|
||||
PtrGlb->PtrComp = PtrGlbNext;
|
||||
PtrGlb->Discr = Ident1;
|
||||
PtrGlb->EnumComp = Ident3;
|
||||
PtrGlb->IntComp = 40;
|
||||
strcpy(PtrGlb->StringComp, "DHRYSTONE PROGRAM, SOME STRING");
|
||||
strcpy(String1Loc, "DHRYSTONE PROGRAM, 1'ST STRING"); /*GOOF*/
|
||||
Array2Glob[8][7] = 10; /* Was missing in published program */
|
||||
for (i = 0; i < LOOPS; ++i) {
|
||||
Proc5();
|
||||
Proc4();
|
||||
IntLoc1 = 2;
|
||||
IntLoc2 = 3;
|
||||
strcpy(String2Loc, "DHRYSTONE PROGRAM, 2'ND STRING");
|
||||
EnumLoc = Ident2;
|
||||
BoolGlob = !Func2(String1Loc, String2Loc);
|
||||
while (IntLoc1 < IntLoc2) {
|
||||
IntLoc3 = 5 * IntLoc1 - IntLoc2;
|
||||
Proc7(IntLoc1, IntLoc2, &IntLoc3);
|
||||
++IntLoc1;
|
||||
}
|
||||
Proc8(Array1Glob, Array2Glob, IntLoc1, IntLoc3);
|
||||
Proc1(PtrGlb);
|
||||
for (CharIndex = 'A'; CharIndex <= Char2Glob; ++CharIndex)
|
||||
if (EnumLoc == Func1(CharIndex, 'C'))
|
||||
Proc6(Ident1, &EnumLoc);
|
||||
IntLoc3 = IntLoc2 * IntLoc1;
|
||||
IntLoc2 = IntLoc3 / IntLoc1;
|
||||
IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1;
|
||||
Proc2(&IntLoc1);
|
||||
}
|
||||
}
|
||||
int main(void) {
|
||||
// TRISG = 0;
|
||||
//PORTG = 0;
|
||||
int i;
|
||||
//while(1){
|
||||
// LATGINV = 0x0001;
|
||||
for(i = 0; i < 100; i++){
|
||||
Proc0();
|
||||
}
|
||||
//}
|
||||
|
||||
}
|
||||
@ -1,241 +0,0 @@
|
||||
/***************************************************************************
|
||||
*
|
||||
* Name : Whetstone
|
||||
* Purpose : Benchmark the Whetstone code. The code focuses on scientific
|
||||
* functions such as sine, cosine, exponents and logarithm on
|
||||
* fixed and floating point numbers.
|
||||
*
|
||||
***************************************************************************/
|
||||
/* List of changes (since TI slaa205b.pdf):
|
||||
- void type of PA(), P0(), P3()
|
||||
- dummy functions to prevent optimization
|
||||
- float (not double) variants of math funcions and constants (this
|
||||
is no matter for AVR)
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
|
||||
#ifdef __AVR__
|
||||
# define atanf(x) atan(x)
|
||||
# define cosf(x) cos(x)
|
||||
# define expf(x) exp(x)
|
||||
# define logf(x) log(x)
|
||||
# define sinf(x) sin(x)
|
||||
# define sqrtf(x) sqrt(x)
|
||||
#endif
|
||||
|
||||
/* Extern functions. To prevent compiler's optimization. */
|
||||
void dummy1 (float x ){}
|
||||
void dummy2 (float x , float y){}
|
||||
|
||||
void PA (float E[5]);
|
||||
void P0 (void);
|
||||
void P3 (float *X, float *Y, float *Z);
|
||||
|
||||
float T, T1, T2, E1[5];
|
||||
int J, K, L;
|
||||
float X1, X2, X3, X4;
|
||||
long ptime, time0;
|
||||
|
||||
int main()
|
||||
{
|
||||
//TRISG = 0;
|
||||
//PORTG = 0;
|
||||
int i;
|
||||
//while(1){
|
||||
//LATGINV = 0x0001;
|
||||
for(i = 0; i < 100; i++){
|
||||
|
||||
int LOOP, I, II, JJ, N1, N2, N3, N4, N5, N6, N7, N8, N9, N10, N11;
|
||||
float X, Y, Z;
|
||||
T = .499975;
|
||||
T1 = 0.50025;
|
||||
T2 = 2.0;
|
||||
LOOP = 1;
|
||||
II = 1;
|
||||
|
||||
for (JJ = 1; JJ <= II; JJ++) {
|
||||
N1 = 0;
|
||||
N2 = 2 * LOOP;
|
||||
N3 = 2 * LOOP;
|
||||
N4 = 2 * LOOP;
|
||||
N5 = 0;
|
||||
N6 = 2 * LOOP;
|
||||
N7 = 2 * LOOP;
|
||||
N8 = 2 * LOOP;
|
||||
N9 = 2 * LOOP;
|
||||
N10 = 0;
|
||||
N11 = 2 * LOOP;
|
||||
|
||||
/* Module 1: Simple identifiers */
|
||||
X1 = 1.0;
|
||||
X2 = -1.0;
|
||||
X3 = -1.0;
|
||||
X4 = -1.0;
|
||||
if (N1 != 0) {
|
||||
for (I = 1; I <= N1; I++) {
|
||||
X1 = (X1 + X2 + X3 - X4) * T;
|
||||
X2 = (X1 + X2 - X3 + X4) * T;
|
||||
X3 = (X1 - X2 + X3 + X4) * T;
|
||||
X4 = (-X1 + X2 + X3 + X4) * T;
|
||||
}
|
||||
}
|
||||
|
||||
/* Module 2: Array elements */
|
||||
E1[1] = 1.0;
|
||||
E1[2] = -1.0;
|
||||
E1[3] = -1.0;
|
||||
E1[4] = -1.0;
|
||||
if (N2 != 0) {
|
||||
for (I = 1; I <= N2; I++) {
|
||||
E1[1] = (E1[1] + E1[2] + E1[3] - E1[4]) * T;
|
||||
E1[2] = (E1[1] + E1[2] - E1[3] + E1[4]) * T;
|
||||
E1[3] = (E1[1] - E1[2] + E1[3] + E1[4]) * T;
|
||||
E1[4] = (-E1[1] + E1[2] + E1[3] + E1[4]) * T;
|
||||
}
|
||||
}
|
||||
|
||||
/* Module 3: Array as parameter */
|
||||
if (N3 != 0) {
|
||||
for (I = 1; I <= N3; I++) {
|
||||
PA (E1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Module 4: Conditional jumps */
|
||||
J = 1;
|
||||
if (N4 != 0) {
|
||||
for (I = 1; I <= N4; I++) {
|
||||
if (J == 1)
|
||||
goto L51;
|
||||
J = 3;
|
||||
goto L52;
|
||||
L51:
|
||||
J = 2;
|
||||
L52:
|
||||
if (J > 2)
|
||||
goto L53;
|
||||
J = 1;
|
||||
goto L54;
|
||||
L53:
|
||||
J = 0;
|
||||
L54:
|
||||
if (J < 1)
|
||||
goto L55;
|
||||
J = 0;
|
||||
goto L60;
|
||||
L55:
|
||||
J = 1;
|
||||
L60: ;
|
||||
}
|
||||
}
|
||||
|
||||
/* Module 5: Integer arithmetic */
|
||||
J = 1;
|
||||
K = 2;
|
||||
L = 3;
|
||||
if (N6 != 0) { /* ??? Where is N5? */
|
||||
for (I = 1; I <= N6; I++) {
|
||||
J = J * (K - J) * (L - K);
|
||||
K = L * K - (L - J) * K;
|
||||
L = (L - K) * (K + J);
|
||||
E1[L - 1] = J + K + L;
|
||||
E1[K - 1] = J * K * L;
|
||||
}
|
||||
}
|
||||
|
||||
/* Module 6: Trigonometric functions */
|
||||
X = 0.5;
|
||||
Y = 0.5;
|
||||
if (N7 != 0) {
|
||||
for (I = 1; I <= N7; I++) {
|
||||
X = T * atanf (T2 * sinf (X) * cosf (X) /
|
||||
(cosf (X + Y) + cosf (X - Y) - 1.0f));
|
||||
Y = T * atanf (T2 * sinf (Y) * cosf (Y) /
|
||||
(cosf (X + Y) + cosf (X - Y) - 1.0f));
|
||||
}
|
||||
}
|
||||
dummy2 (X, Y);
|
||||
|
||||
/* Module 7: Procedure calls */
|
||||
X = 1.0;
|
||||
Y = 1.0;
|
||||
Z = 1.0;
|
||||
if (N8 != 0) {
|
||||
for (I = 1; I <= N8; I++) {
|
||||
P3 (&X, &Y, &Z);
|
||||
}
|
||||
}
|
||||
|
||||
/* Module 8: Array references */
|
||||
J = 1;
|
||||
K = 2;
|
||||
L = 3;
|
||||
E1[1] = 1.0;
|
||||
E1[2] = 2.0;
|
||||
E1[3] = 3.0;
|
||||
if (N9 != 0) {
|
||||
for (I = 1; I <= N9; I++) {
|
||||
P0 ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Module 9: Integer arithmetic */
|
||||
J = 2;
|
||||
K = 3;
|
||||
if (N10 != 0) { /* TI skips this strange test. */
|
||||
for (I = 1; I <= N10; I++) {
|
||||
J = J + K;
|
||||
K = J + K;
|
||||
J = K - J;
|
||||
K = K - J - J;
|
||||
}
|
||||
}
|
||||
|
||||
/* Module 10: Standard functions */
|
||||
X = 0.75;
|
||||
if (N11 != 0) {
|
||||
for (I = 1; I <= N11; I++) {
|
||||
X = sqrtf (expf (logf (X) / T1));
|
||||
}
|
||||
}
|
||||
dummy1 (X);
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
void PA (float E[5])
|
||||
{
|
||||
int J1;
|
||||
J1 = 0;
|
||||
L10:
|
||||
E[1] = (E[1] + E[2] + E[3] - E[4]) * T;
|
||||
E[2] = (E[1] + E[2] - E[3] + E[4]) * T;
|
||||
E[3] = (E[1] - E[2] + E[3] + E[4]) * T;
|
||||
E[4] = (-E[1] + E[2] + E[3] + E[4]) / T2;
|
||||
J1 = J1 + 1;
|
||||
if ((J1 - 6) < 0)
|
||||
goto L10;
|
||||
return;
|
||||
}
|
||||
|
||||
void P0 ()
|
||||
{
|
||||
E1[J] = E1[K];
|
||||
E1[K] = E1[L];
|
||||
E1[L] = E1[J];
|
||||
return;
|
||||
}
|
||||
|
||||
void P3 (float *X, float *Y, float *Z)
|
||||
{
|
||||
float Y1;
|
||||
X1 = *X;
|
||||
Y1 = *Y;
|
||||
X1 = T * (X1 + Y1);
|
||||
Y1 = T * (X1 + Y1);
|
||||
*Z = (X1 + Y1) / T2;
|
||||
return;
|
||||
}
|
||||
24
testcases/assignment.c
Normal file
24
testcases/assignment.c
Normal file
@ -0,0 +1,24 @@
|
||||
#define A (unsigned long)4
|
||||
#define STUFF int c = a + b
|
||||
|
||||
#warning butts
|
||||
|
||||
#include "stuff.h"
|
||||
|
||||
void g()
|
||||
{
|
||||
int a = 4;
|
||||
int b = 6;
|
||||
|
||||
int c = a + b;
|
||||
(void)c;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
short a = A;
|
||||
char b = 3;
|
||||
|
||||
STUFF;
|
||||
int c2 = a + A;
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
{"fir.c": [{"operation_name": "<", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 324}, {"operation_name": "++", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 324}, {"operation_name": "=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 37}, {"operation_name": "=", "type_lhs": "volatile float", "type_rhs": "float", "type_result": "float", "count": 360}, {"operation_name": "/", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 360}, {"operation_name": "+", "type_lhs": "float", "type_rhs": "float", "type_result": "float", "count": 324}, {"operation_name": "*", "type_lhs": "float", "type_rhs": "float", "type_result": "float", "count": 324}, {"operation_name": "subscript", "type_lhs": "const float *", "type_rhs": "int", "type_result": "const float", "count": 324}, {"operation_name": "+", "type_lhs": "unsigned int", "type_rhs": "unsigned int", "type_result": "unsigned int", "count": 288}, {"operation_name": "subscript", "type_lhs": "const unsigned int *", "type_rhs": "int", "type_result": "const unsigned int", "count": 612}, {"operation_name": "-", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 288}, {"operation_name": "+", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 612}, {"operation_name": "subscript", "type_lhs": "volatile float *", "type_rhs": "int", "type_result": "volatile float", "count": 36}]}
|
||||
@ -1,7 +0,0 @@
|
||||
int main()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 0; i++)
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
{"for_loop.c": [{"operation_name": "<", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 0}, {"operation_name": "++", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 0}, {"operation_name": "=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 1}]}
|
||||
@ -1,8 +1,7 @@
|
||||
|
||||
//#include <stdlib.h>
|
||||
// TODO: stddef.h not found.
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(){
|
||||
int gauss_blur(){
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1 +0,0 @@
|
||||
{"gauss_blur.c": [{"operation_name": "=", "type_lhs": "unsigned char *", "type_rhs": "unsigned char *", "type_result": "unsigned char *", "count": 4}, {"function_name": "malloc", "call_result_type": "void *", "count": 6}, {"operation_name": "*", "type_lhs": "unsigned long", "type_rhs": "unsigned long", "type_result": "unsigned long", "count": 6}, {"operation_name": "*", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 36868}, {"operation_name": "=", "type_lhs": "unsigned char **", "type_rhs": "unsigned char **", "type_result": "unsigned char **", "count": 2}, {"operation_name": "=", "type_lhs": "unsigned char", "type_rhs": "unsigned char", "type_result": "unsigned char", "count": 5000}, {"operation_name": "subscript", "type_lhs": "unsigned char *", "type_rhs": "int", "type_result": "unsigned char", "count": 41864}, {"operation_name": "==", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 18}, {"operation_name": "<", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 18824}, {"operation_name": "++", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 69512}, {"operation_name": "=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 18820}, {"operation_name": "-", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 50688}, {"operation_name": "<=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 50688}, {"operation_name": "-", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 18432}, {"operation_name": "||", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 36864}, {"operation_name": "!=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 73728}, {"operation_name": "=", "type_lhs": "char", "type_rhs": "char", "type_result": "char", "count": 36864}, {"function_name": "abs", "call_result_type": "int", "count": 36864}, {"operation_name": "+", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 110592}, {"operation_name": ">", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 8680}, {"function_name": "free", "call_result_type": "void", "count": 4}]}
|
||||
@ -1,54 +0,0 @@
|
||||
|
||||
int main()
|
||||
{
|
||||
int a, b, c;
|
||||
b = 10;
|
||||
c = 15;
|
||||
|
||||
|
||||
// F
|
||||
if (c == 10)
|
||||
{
|
||||
a = 5;
|
||||
}
|
||||
|
||||
// F
|
||||
if (c == 10)
|
||||
{
|
||||
a = 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = 15;
|
||||
}
|
||||
|
||||
// F T
|
||||
if (c == 10 || b == 10)
|
||||
{
|
||||
a = 10;
|
||||
}
|
||||
|
||||
// T T (not taken)
|
||||
if (c == 15 || b == 10)
|
||||
{
|
||||
a = 10;
|
||||
}
|
||||
|
||||
// T T
|
||||
if (c == 15 && b == 10)
|
||||
{
|
||||
a = 10;
|
||||
}
|
||||
|
||||
// T F
|
||||
if (c == 15 && b == 15)
|
||||
{
|
||||
a = 10;
|
||||
}
|
||||
|
||||
// F T
|
||||
if (c == 10 && b == 15)
|
||||
{
|
||||
a = 10;
|
||||
}
|
||||
}
|
||||
@ -17,12 +17,12 @@ int main(void)
|
||||
{
|
||||
int m, n, p;
|
||||
volatile UInt16 m3[3][5];
|
||||
for(m = 0; m < 3; m++) // 1 + 3 * 2 // 3 * 3
|
||||
for(m = 0; m < 3; m++)
|
||||
{
|
||||
for(p = 0; p < 5; p++) // 3 + 15 * 2 // 15 * 3
|
||||
for(p = 0; p < 5; p++)
|
||||
{
|
||||
m3[m][p] = 0; // 15: BasicOperation(operation_name='=', type_lhs='unsigned short', type_rhs='unsigned short', type_result='unsigned short')
|
||||
for(n = 0; n < 4; n++) // 15 + 60 * 2 // 60 * 3
|
||||
m3[m][p] = 0;
|
||||
for(n = 0; n < 4; n++)
|
||||
{
|
||||
m3[m][p] += m1[m][n] * m2[n][p];
|
||||
}
|
||||
|
||||
@ -1 +0,0 @@
|
||||
{"matrix.c": [{"operation_name": "<", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 78}, {"operation_name": "++", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 78}, {"operation_name": "=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 19}, {"operation_name": "=", "type_lhs": "unsigned short", "type_rhs": "unsigned short", "type_result": "unsigned short", "count": 15}, {"operation_name": "subscript", "type_lhs": "volatile UInt16 *", "type_rhs": "int", "type_result": "unsigned short", "count": 75}, {"operation_name": "subscript", "type_lhs": "volatile UInt16 (*)[5]", "type_rhs": "int", "type_result": "volatile UInt16 [5]", "count": 75}, {"operation_name": "*", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 60}, {"operation_name": "subscript", "type_lhs": "const UInt16 *", "type_rhs": "int", "type_result": "unsigned short", "count": 120}, {"operation_name": "subscript", "type_lhs": "const UInt16 (*)[4]", "type_rhs": "int", "type_result": "const UInt16 [4]", "count": 60}, {"operation_name": "subscript", "type_lhs": "const UInt16 (*)[5]", "type_rhs": "int", "type_result": "const UInt16 [5]", "count": 60}]}
|
||||
17
testcases/stuff.h
Normal file
17
testcases/stuff.h
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by erki on 23.02.21.
|
||||
//
|
||||
|
||||
#ifndef C_ANALYZER_STUFF_H
|
||||
#define C_ANALYZER_STUFF_H
|
||||
|
||||
void f()
|
||||
{
|
||||
int a = 4;
|
||||
int b = 6;
|
||||
|
||||
int c = a + b;
|
||||
(void)c;
|
||||
}
|
||||
|
||||
#endif //C_ANALYZER_STUFF_H
|
||||
Loading…
x
Reference in New Issue
Block a user