diff --git a/op-finder/CMakeLists.txt b/op-finder/CMakeLists.txt index 9e2285e..15668a8 100644 --- a/op-finder/CMakeLists.txt +++ b/op-finder/CMakeLists.txt @@ -25,7 +25,7 @@ add_executable(op-finder OperationFinderAstVisitor.cpp OperationFinderAstConsumer.cpp OperationFinderAstAction.cpp -) + OperationLog.cpp) target_include_directories(op-finder PRIVATE diff --git a/op-finder/OperationFinder.cpp b/op-finder/OperationFinder.cpp index 431efc2..dc5e6bd 100644 --- a/op-finder/OperationFinder.cpp +++ b/op-finder/OperationFinder.cpp @@ -11,6 +11,35 @@ using namespace clang; using namespace clang::ast_matchers; +namespace +{ + +std::string ResolveTypeName(const QualType& t) +{ + if (t->isTypedefNameType()) + { + const TypedefType* tdt = cast(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) { @@ -28,13 +57,12 @@ void OperationFinder::processArithmetic(const clang::BinaryOperator* op, const c OperationLog log; log.line = line_number; - log.operation = getOpcode(op).str(); + log.entry_type = OperationLog::BasicOperation::TYPE_NAME; + log.entry = _createBasicOperationLogEntry(op, op->getLHS(), op->getRHS()); log.current_for_loops = _for_loop_stack; log.is_fallthrough = _in_fallthrough; - _processExpressionTypes(log, op, op->getLHS(), op->getRHS()); - - _storage->pushOperation(file_name, log); + _storage->pushOperation(file_name, std::move(log)); llvm::outs() << "\n"; } @@ -55,17 +83,47 @@ void OperationFinder::processUnaryArithmetic(const clang::UnaryOperator* op, con OperationLog log; log.line = line_number; - log.operation = getOpcode(op).str(); + log.entry_type = OperationLog::BasicOperation::TYPE_NAME; + log.entry = _createBasicOperationLogEntry(op, lhs, nullptr); log.current_for_loops = _for_loop_stack; log.is_fallthrough = _in_fallthrough; - _processExpressionTypes(log, op, lhs, nullptr); - - _storage->pushOperation(file_name, log); + _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); + + const auto [file_name, line_number, column_number] = resolveLocations(call, source_manager); + + const std::string res_type = ResolveTypeName(func->getType()); + const std::string func_name = func->getNameAsString(); + + llvm::outs() << file_name << ":" + << line_number << ":" + << column_number << ":" + << "Function call: name: " << func_name << " Restype: " + << res_type << "\n"; + + auto func_call = std::make_unique(); + func_call->function_name = func_name; + func_call->call_result_type = res_type; + + OperationLog log; + log.line = line_number; + log.entry_type = OperationLog::FunctionCall::TYPE_NAME; + log.entry = std::move(func_call); + log.current_for_loops = _for_loop_stack; + log.is_fallthrough = _in_fallthrough; + + _storage->pushOperation(file_name, std::move(log)); +} + void OperationFinder::fallthroughBranchEntered() { _in_fallthrough = true; @@ -88,44 +146,24 @@ void OperationFinder::forLoopExited() _for_loop_stack.pop_back(); } -void OperationFinder::_processExpressionTypes(OperationLog& log, const Expr* source, const Expr* op1, const Expr* op2) +std::unique_ptr + OperationFinder::_createBasicOperationLogEntry(const Expr* source, const Expr* op1, const Expr* op2) { - - auto printTypeName = [](const QualType& t) -> std::string - { - if (t->isTypedefNameType()) - { - const TypedefType* tdt = cast(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(); - } - }; + auto log = std::make_unique(); llvm::outs() << "Expression types:\n\tExpression eval type: "; - log.operand_result = printTypeName(source->getType()); + log->type_result = ResolveTypeName(source->getType()); if (op1) { llvm::outs() << "\n\tLHS eval type: "; - log.operand_lhs = printTypeName(op1->getType()); + log->type_lhs = ResolveTypeName(op1->getType()); } if (op2) { llvm::outs() << "\n\tRHS eval type: "; - log.operand_rhs = printTypeName(op2->getType()); + log->type_rhs = ResolveTypeName(op2->getType()); } + + return log; } diff --git a/op-finder/OperationFinder.hpp b/op-finder/OperationFinder.hpp index 9a06dee..48ef482 100644 --- a/op-finder/OperationFinder.hpp +++ b/op-finder/OperationFinder.hpp @@ -16,6 +16,7 @@ public: 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 fallthroughBranchEntered(); void fallthroughBranchExited(); @@ -24,7 +25,8 @@ public: void forLoopExited(); private: - void _processExpressionTypes(OperationLog& log, const clang::Expr* source, const clang::Expr* op1, const clang::Expr* op2); + std::unique_ptr + _createBasicOperationLogEntry(const clang::Expr* source, const clang::Expr* op1, const clang::Expr* op2); int _next_for_loop_id = 0; bool _in_fallthrough = false; diff --git a/op-finder/OperationFinderAstVisitor.cpp b/op-finder/OperationFinderAstVisitor.cpp index 4d3e178..ef91717 100644 --- a/op-finder/OperationFinderAstVisitor.cpp +++ b/op-finder/OperationFinderAstVisitor.cpp @@ -43,6 +43,15 @@ bool OperationFinderAstVisitor::VisitUnaryOperator(clang::UnaryOperator* op) return true; } +bool OperationFinderAstVisitor::VisitCallExpr(clang::CallExpr* call) +{ + assert(_context); + + _op_finder->processFunctionCall(call, _context->getSourceManager()); + + return true; +} + bool OperationFinderAstVisitor::dataTraverseStmtPre(clang::Stmt* stmt) { assert(_context); diff --git a/op-finder/OperationFinderAstVisitor.hpp b/op-finder/OperationFinderAstVisitor.hpp index 041a927..d1408f7 100644 --- a/op-finder/OperationFinderAstVisitor.hpp +++ b/op-finder/OperationFinderAstVisitor.hpp @@ -20,6 +20,7 @@ public: bool VisitForStmt(clang::ForStmt* stmt); bool VisitBinaryOperator(clang::BinaryOperator* op); bool VisitUnaryOperator(clang::UnaryOperator* op); + bool VisitCallExpr(clang::CallExpr* call); bool dataTraverseStmtPre(clang::Stmt* stmt); bool dataTraverseStmtPost(clang::Stmt* stmt); diff --git a/op-finder/OperationLog.cpp b/op-finder/OperationLog.cpp new file mode 100644 index 0000000..32a25ed --- /dev/null +++ b/op-finder/OperationLog.cpp @@ -0,0 +1,44 @@ +// +// Created by erki on 06.03.21. +// + +#include "OperationLog.hpp" + +namespace +{ + +template +std::unique_ptr DecodeType(const nlohmann::json& j, const std::string& entry_type) +{ + if (entry_type == T::TYPE_NAME) + { + auto t = std::make_unique(); + 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(j, entry_type))) + return; + else if ((entry = DecodeType(j, entry_type))) + return; +} diff --git a/op-finder/OperationLog.hpp b/op-finder/OperationLog.hpp index cc549f0..5bf6d1e 100644 --- a/op-finder/OperationLog.hpp +++ b/op-finder/OperationLog.hpp @@ -6,16 +6,50 @@ #define C_ANALYZER_OPERATIONLOG_HPP #include +#include + +#include struct OperationLog { - std::string operation; + 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 type_lhs; + std::string type_rhs; + std::string type_result; + + [[nodiscard]] nlohmann::json toJson() const override; + }; + unsigned int line; - std::string operand_lhs; - std::string operand_rhs; - std::string operand_result; + std::string entry_type; + std::unique_ptr entry; + bool is_fallthrough; std::vector current_for_loops; + + OperationLog() = default; + OperationLog(const OperationLog&) = delete; + OperationLog(OperationLog&&) = default; + + void DecodeEntry(const nlohmann::json& j); }; class IOperationOutput @@ -23,19 +57,15 @@ class IOperationOutput public: virtual ~IOperationOutput() = default; - virtual void pushOperation(const std::string& filename, const OperationLog& op) = 0; + virtual void pushOperation(const std::string& filename, OperationLog&& op) = 0; }; -#include - inline void to_json(nlohmann::json& j, const OperationLog& 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}, + {"entry_type", l.entry_type}, + {"entry", l.entry->toJson()}, {"is_fallthrough", l.is_fallthrough}, {"current_for_loops", l.current_for_loops} }; @@ -43,13 +73,42 @@ inline void to_json(nlohmann::json& j, const OperationLog& l) inline void from_json(const nlohmann::json& j, OperationLog& 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); + j.at("entry_type").get_to(l.entry_type); + l.DecodeEntry(j["entry"]); + j.at("is_fallthrough").get_to(l.is_fallthrough); j.at("current_for_loops").get_to(l.current_for_loops); } +inline void to_json(nlohmann::json& j, const OperationLog::BasicOperation& bo) +{ + j = nlohmann::json{ + {"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("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 diff --git a/op-finder/OperationStorage.cpp b/op-finder/OperationStorage.cpp index dd16a43..cdc0bb0 100644 --- a/op-finder/OperationStorage.cpp +++ b/op-finder/OperationStorage.cpp @@ -18,14 +18,19 @@ OperationStorage::~OperationStorage() _dumpToFile(); } -void OperationStorage::pushOperation(const std::string& filename, const OperationLog& op) +void OperationStorage::enablePrettyPrint() +{ + _pretty_print = true; +} + +void OperationStorage::pushOperation(const std::string& filename, OperationLog&& op) { auto it = _operations.find(filename); if (it == _operations.end()) - it = _operations.insert({ filename, {} }).first; + it = _operations.emplace(filename, std::vector()).first; - it->second.push_back(op); + it->second.emplace_back(std::move(op)); } const std::unordered_map>& OperationStorage::getOperations() const @@ -39,5 +44,8 @@ void OperationStorage::_dumpToFile() std::ofstream file(_output_filename); - file << json; + if (_pretty_print) + file << std::setw(4) << json; + else + file << json; } diff --git a/op-finder/OperationStorage.hpp b/op-finder/OperationStorage.hpp index 809bd1a..fddee49 100644 --- a/op-finder/OperationStorage.hpp +++ b/op-finder/OperationStorage.hpp @@ -20,12 +20,15 @@ public: explicit OperationStorage(const std::string& output_filename); ~OperationStorage() override; - void pushOperation(const std::string& filename, const OperationLog& op) override; + void enablePrettyPrint(); + void pushOperation(const std::string& filename, OperationLog&& op) override; [[nodiscard]] const std::unordered_map>& getOperations() const; private: std::unordered_map> _operations; std::string _output_filename; + bool _pretty_print = false; + void _dumpToFile(); }; diff --git a/op-finder/main.cpp b/op-finder/main.cpp index 1a58772..16973ac 100644 --- a/op-finder/main.cpp +++ b/op-finder/main.cpp @@ -24,6 +24,8 @@ static cl::opt OutputFile("o", cl::desc("File to output the JSON to static cl::opt RootDirectory("r", cl::desc("The root directory of the source files."), cl::cat(MyToolCategory)); +static cl::opt PrettyPrint("pretty", cl::desc("Pretty-print the output JSON.")); + // 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. @@ -50,6 +52,9 @@ int main(int argc, const char** argv) OperationStorage storage(OutputFile.getValue()); + if (PrettyPrint.getValue()) + storage.enablePrettyPrint(); + OperationFinder op_finder(&storage); #if 0