diff --git a/op-finder/CMakeLists.txt b/op-finder/CMakeLists.txt index 2a22a68..b21bc24 100644 --- a/op-finder/CMakeLists.txt +++ b/op-finder/CMakeLists.txt @@ -20,6 +20,7 @@ llvm_map_components_to_libnames(llvm_libs support option core) add_executable(op-finder main.cpp OperationFinder.cpp + OperationStorage.cpp ) target_include_directories(op-finder diff --git a/op-finder/OperationFinder.cpp b/op-finder/OperationFinder.cpp index bc6eecf..1150d50 100644 --- a/op-finder/OperationFinder.cpp +++ b/op-finder/OperationFinder.cpp @@ -5,7 +5,6 @@ #include "OperationFinder.hpp" #include -#include using namespace clang; using namespace clang::ast_matchers; @@ -50,17 +49,32 @@ SourceLocation ResolveOperationSourceLocation(const SourceManager& source_manage return original; } +template +std::tuple resolveLocations(const TOp* op, const 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) + }; } +} + +OperationFinder::OperationFinder(const std::string& output_dir) + : _storage(output_dir) +{ } + 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) +void OperationFinder::run(const MatchFinder::MatchResult& result) { if (result.Nodes.getNodeAs("assignment")) { @@ -70,10 +84,6 @@ void OperationFinder::run(const MatchFinder::MatchResult &result) { _processArithmetic(result); } - else if (result.Nodes.getNodeAs("compound_stmt")) - { - _processCompoundStmt(result); - } else if (result.Nodes.getNodeAs("unary_arithmetic")) { _processUnaryArithmetic(result); @@ -86,15 +96,20 @@ void OperationFinder::_processAssignment(const clang::ast_matchers::MatchFinder: assert(op); - const auto& source_manager = *result.SourceManager; - const auto& loc = ResolveOperationSourceLocation(source_manager, op->getBeginLoc()); + const auto [file_name, line_number, column_number] = resolveLocations(op, *result.SourceManager); - llvm::outs() << source_manager.getFilename(loc) << ":" - << source_manager.getSpellingLineNumber(loc) << ":" - << source_manager.getSpellingColumnNumber(loc) << ":" + llvm::outs() << file_name << ":" + << line_number << ":" + << column_number << ":" << "Assignment: Assigned to: "; - _processExpressionTypes(op, op->getLHS(), op->getRHS()); + OperationStorage::Log log; + log.line = line_number; + log.operation = "="; + + _processExpressionTypes(log, op, op->getLHS(), op->getRHS()); + + _storage.pushOperation(file_name, log); llvm::outs() << "\n"; } @@ -105,39 +120,20 @@ void OperationFinder::_processArithmetic(const MatchFinder::MatchResult& result) assert(op); - const auto& source_manager = *result.SourceManager; - const auto& loc = ResolveOperationSourceLocation(source_manager, op->getBeginLoc()); + const auto [file_name, line_number, column_number] = resolveLocations(op, *result.SourceManager); - llvm::outs() << source_manager.getFilename(loc) << ":" - << source_manager.getSpellingLineNumber(loc) << ":" - << source_manager.getSpellingColumnNumber(loc) << ":" - << "Arithmetic: Type: " << getOpcode(op) << " LHS: "; + llvm::outs() << file_name << ":" + << line_number << ":" + << column_number << ":" + << "Binary arithmetic: Type: " << getOpcode(op) << " LHS: "; - _processExpressionTypes(op, op->getLHS(), op->getRHS()); + OperationStorage::Log log; + log.line = line_number; + log.operation = getOpcode(op).str(); - llvm::outs() << "\n"; -} + _processExpressionTypes(log, op, op->getLHS(), op->getRHS()); -void OperationFinder::_processCompoundStmt(const clang::ast_matchers::MatchFinder::MatchResult& result) -{ - const CompoundStmt* stmt = result.Nodes.getNodeAs("compound_stmt"); - - assert(stmt); - - const CompoundAssignOperator* op = dyn_cast(stmt->getStmtExprResult()); - - if (!op || !op->isCompoundAssignmentOp()) - return; - - const auto& source_manager = *result.SourceManager; - const auto& loc = ResolveOperationSourceLocation(source_manager, 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()); + _storage.pushOperation(file_name, log); llvm::outs() << "\n"; } @@ -150,22 +146,28 @@ void OperationFinder::_processUnaryArithmetic(const MatchFinder::MatchResult &re assert(op); assert(lhs); - const auto& source_manager = *result.SourceManager; - const auto& loc = ResolveOperationSourceLocation(source_manager, op->getBeginLoc()); + const auto [file_name, line_number, column_number] = resolveLocations(op, *result.SourceManager); - llvm::outs() << source_manager.getFilename(loc) << ":" - << source_manager.getSpellingLineNumber(loc) << ":" - << source_manager.getSpellingColumnNumber(loc) << ":" + llvm::outs() << file_name << ":" + << line_number << ":" + << column_number << ":" << "Unary arithmetic: Type: " << getOpcode(op) << " LHS: "; - _processExpressionTypes(op, lhs, nullptr); + OperationStorage::Log log; + log.line = line_number; + log.operation = getOpcode(op).str(); + + _processExpressionTypes(log, op, lhs, nullptr); + + _storage.pushOperation(file_name, log); llvm::outs() << "\n"; } -void OperationFinder::_processExpressionTypes(const Expr* source, const Expr* op1, const Expr* op2) +void OperationFinder::_processExpressionTypes(OperationStorage::Log& log, const Expr* source, const Expr* op1, const Expr* op2) { - auto printTypeName = [](const QualType& t) -> void + + auto printTypeName = [](const QualType& t) -> std::string { if (t->isTypedefNameType()) { @@ -174,28 +176,32 @@ void OperationFinder::_processExpressionTypes(const Expr* source, const Expr* op 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(); } }; llvm::outs() << "Expression types:\n\tExpression eval type: "; - printTypeName(source->getType()); + log.operand_result = printTypeName(source->getType()); if (op1) { llvm::outs() << "\n\tLHS eval type: "; - printTypeName(op1->getType()); + log.operand_lhs = printTypeName(op1->getType()); } if (op2) { llvm::outs() << "\n\tRHS eval type: "; - printTypeName(op2->getType()); + log.operand_rhs = printTypeName(op2->getType()); } } diff --git a/op-finder/OperationFinder.hpp b/op-finder/OperationFinder.hpp index 4242c00..b3cb68b 100644 --- a/op-finder/OperationFinder.hpp +++ b/op-finder/OperationFinder.hpp @@ -8,19 +8,24 @@ #include #include +#include "OperationStorage.hpp" + class OperationFinder : public clang::ast_matchers::MatchFinder::MatchCallback { public: + OperationFinder(const std::string& output_dir); + void addMatcher(clang::ast_matchers::MatchFinder& finder); - virtual void run(const clang::ast_matchers::MatchFinder::MatchResult& result) override; + 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); - void _processExpressionTypes(const clang::Expr* source, const clang::Expr* op1, const clang::Expr* op2); + void _processExpressionTypes(OperationStorage::Log& log, const clang::Expr* source, const clang::Expr* op1, const clang::Expr* op2); + + OperationStorage _storage; }; #endif //LLVM_PROTO_OPERATIONFINDER_HPP diff --git a/op-finder/OperationStorage.cpp b/op-finder/OperationStorage.cpp new file mode 100644 index 0000000..ef74dfc --- /dev/null +++ b/op-finder/OperationStorage.cpp @@ -0,0 +1,64 @@ +// +// Created by erki on 28.02.21. +// + +#include "OperationStorage.hpp" + +#include +#include + +#include +#include + +void to_json(nlohmann::json& j, const OperationStorage::Log& l) +{ + j = nlohmann::json{ + {"operation", l.operation}, + {"line", l.line}, + {"operand_lhs", l.operand_lhs}, + {"operand_rhs", l.operand_rhs}, + {"operand_result", l.operand_result} + }; +} + +void from_json(const nlohmann::json& j, OperationStorage::Log& l) +{ + j.at("operation").get_to(l.operation); + j.at("line").get_to(l.line); + j.at("operand_lhs").get_to(l.operand_lhs); + j.at("operand_rhs").get_to(l.operand_rhs); + j.at("operand_result").get_to(l.operand_result); +} + +OperationStorage::OperationStorage(const std::string& output_filename) + : _output_filename(output_filename) +{ } + +OperationStorage::~OperationStorage() +{ + _dumpToFile(); +} + +void OperationStorage::pushOperation(const std::string& filename, const Log& op) +{ + auto it = _operations.find(filename); + + if (it == _operations.end()) + it = _operations.insert({ filename, {} }).first; + + it->second.push_back(op); +} + +const std::unordered_map>& OperationStorage::getOperations() const +{ + return _operations; +} + +void OperationStorage::_dumpToFile() +{ + nlohmann::json json = _operations; + + std::ofstream file(_output_filename); + + file << json; +} diff --git a/op-finder/OperationStorage.hpp b/op-finder/OperationStorage.hpp new file mode 100644 index 0000000..0b0e7ef --- /dev/null +++ b/op-finder/OperationStorage.hpp @@ -0,0 +1,39 @@ +// +// Created by erki on 28.02.21. +// + +#ifndef C_ANALYZER_OPERATIONSTORAGE_HPP +#define C_ANALYZER_OPERATIONSTORAGE_HPP + +#include +#include +#include + +class OperationStorage +{ +public: + struct Log + { + std::string operation; + unsigned int line; + std::string operand_lhs; + std::string operand_rhs; + std::string operand_result; + }; + + OperationStorage() = delete; + OperationStorage(const OperationStorage&) = delete; + + explicit OperationStorage(const std::string& output_filename); + ~OperationStorage(); + + void pushOperation(const std::string& filename, const Log& op); + [[nodiscard]] const std::unordered_map>& getOperations() const; +private: + std::unordered_map> _operations; + std::string _output_filename; + + void _dumpToFile(); +}; + +#endif //C_ANALYZER_OPERATIONSTORAGE_HPP diff --git a/op-finder/main.cpp b/op-finder/main.cpp index 8aa6d3c..c9cb36d 100644 --- a/op-finder/main.cpp +++ b/op-finder/main.cpp @@ -13,15 +13,20 @@ using namespace llvm; // Apply a custom category to all command-line options so that they are the // only ones displayed. -static llvm::cl::OptionCategory MyToolCategory("opcounter options"); +static llvm::cl::OptionCategory MyToolCategory("op-finder options"); + +static llvm::cl::opt OutputFile("o", cl::desc("File to output the JSON to."), + cl::cat(MyToolCategory)); // CommonOptionsParser declares HelpMessage with a description of the common // command-line options related to the compilation database and input files. // It's nice to have this help message in all tools. -static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); +static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); // A help message for this specific tool can be added afterwards. -static cl::extrahelp MoreHelp("\nMore help text...\n"); +static llvm::cl::extrahelp MoreHelp("\nThe program takes the input ... files, parses their\n" + "AST and outputs a singular file containing a list of all noteworthy operations\n" + "for later analysis.\n"); int main(int argc, const char** argv) { @@ -37,7 +42,7 @@ int main(int argc, const char** argv) ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); - OperationFinder op_finder; + OperationFinder op_finder(OutputFile.getValue()); MatchFinder finder; op_finder.addMatcher(finder);