citrun

watch C/C++ source code execute
Log | Files | Refs | LICENSE

commit 76c7d38139569615270925380ff8a805cfd7aecc
parent 766b1869141bb3450fa8bb47a2badf46956c9cf1
Author: Kyle Milz <kyle@0x30.net>
Date:   Sun,  7 Aug 2016 18:11:35 -0600

src: improve logging from ast visitor

- also nuke inst_sites from the runtime node, it's not necessary

Diffstat:
Mlib/runtime.c | 2--
Mlib/runtime.h | 1-
Msrc/inst_action.cc | 32+++++++++++++++++++++++++++-----
Msrc/inst_action.h | 28+++++++++++++++++++++++++++-
Msrc/inst_ast_visitor.cc | 56++++++++++++++++++++++++++++++++++++++++----------------
Msrc/inst_ast_visitor.h | 30+++++++++++++++++++++++++-----
Msrc/inst_main.cc | 4+++-
7 files changed, 122 insertions(+), 31 deletions(-)

diff --git a/lib/runtime.c b/lib/runtime.c @@ -158,7 +158,6 @@ xwrite(int d, const void *buf, size_t bytes_total) * - length of source file name * - source file name * - size of the execution counters - * - number of instrumentation sites. */ static void send_static(int fd) @@ -203,7 +202,6 @@ send_static(int fd) xwrite(fd, &sz, sizeof(sz)); xwrite(fd, node.file_name, sz); xwrite(fd, &node.size, sizeof(node.size)); - xwrite(fd, &node.inst_sites, sizeof(node.size)); } assert(i == nodes_total); assert(w == NULL); diff --git a/lib/runtime.h b/lib/runtime.h @@ -4,7 +4,6 @@ static uint8_t citrun_minor = 0; struct citrun_node { uint64_t *lines_ptr; uint32_t size; - uint32_t inst_sites; const char *file_name; struct citrun_node *next; uint64_t *old_lines; diff --git a/src/inst_action.cc b/src/inst_action.cc @@ -39,17 +39,16 @@ InstrumentAction::EndSourceFileAction() { clang::SourceManager &sm = m_TheRewriter.getSourceMgr(); const clang::FileID main_fid = sm.getMainFileID(); - std::stringstream ss; clang::SourceLocation start = sm.getLocForStartOfFile(main_fid); clang::SourceLocation end = sm.getLocForEndOfFile(main_fid); unsigned int num_lines = sm.getPresumedLineNumber(end); - int rw_count = m_InstrumentASTConsumer->get_visitor().GetRewriteCount(); std::string const file_name = getCurrentFile(); // Write instrumentation preamble. Includes runtime header, per tu // citrun_node and static constructor for runtime initialization. + std::stringstream ss; ss << "#ifdef __cplusplus" << std::endl << "extern \"C\" {" << std::endl << "#endif" << std::endl; @@ -58,7 +57,6 @@ InstrumentAction::EndSourceFileAction() ss << "static struct citrun_node _citrun_node = {" << std::endl << " _citrun_lines," << std::endl << " " << num_lines << "," << std::endl - << " " << rw_count << "," << std::endl << " \"" << file_name << "\"," << std::endl; ss << "};" << std::endl; ss << "__attribute__((constructor))" << std::endl @@ -68,16 +66,40 @@ InstrumentAction::EndSourceFileAction() ss << "#ifdef __cplusplus" << std::endl << "}" << std::endl << "#endif" << std::endl; - m_TheRewriter.InsertTextAfter(start, ss.str()); + + std::string header = ss.str(); + unsigned header_sz = std::count(header.begin(), header.end(), '\n'); + + if (m_TheRewriter.InsertTextAfter(start, header)) { + *m_log << m_pfx << "Failed inserting " << header_sz + << " lines of instrumentation preabmle."; + return; + } + + RewriteASTVisitor v = m_InstrumentASTConsumer->get_visitor(); + *m_log << m_pfx << "Instrumentation of '" << file_name << "' finished:\n"; + *m_log << m_pfx << " " << num_lines << " Lines of source code\n"; + *m_log << m_pfx << " " << header_sz << " Lines of instrumentation header\n"; + *m_log << m_pfx << " " << v.m_mainfunc << " Functions called 'main'\n"; + *m_log << m_pfx << " " << v.m_funcdecl << " Function declarations\n"; + *m_log << m_pfx << " " << v.m_ifstmt << " If statements\n"; + *m_log << m_pfx << " " << v.m_forstmt << " For statements\n"; + *m_log << m_pfx << " " << v.m_whilestmt << " While statements\n"; + *m_log << m_pfx << " " << v.m_switchstmt << " Switch statements\n"; + *m_log << m_pfx << " " << v.m_returnstmt << " Return statement values\n"; + *m_log << m_pfx << " " << v.m_callexpr << " Call expressions\n"; + *m_log << m_pfx << " " << v.m_totalstmt << " Total statements in source\n"; std::error_code ec; llvm::raw_fd_ostream output(file_name, ec, llvm::sys::fs::F_None); - if (ec.value()) { + *m_log << m_pfx << "Error writing modified source: " + << ec.message() << "\n"; warnx("'%s': %s", file_name.c_str(), ec.message().c_str()); return; } // Write the instrumented source file m_TheRewriter.getEditBuffer(main_fid).write(output); + *m_log << m_pfx << "Modified source written successfully.\n"; } diff --git a/src/inst_action.h b/src/inst_action.h @@ -1,6 +1,7 @@ #include <clang/AST/ASTConsumer.h> #include <clang/Frontend/FrontendActions.h> #include <clang/Rewrite/Core/Rewriter.h> +#include <clang/Tooling/Tooling.h> #include "inst_ast_visitor.h" @@ -27,7 +28,11 @@ private: // For each source file provided to the tool, a new FrontendAction is created. class InstrumentAction : public clang::ASTFrontendAction { public: - InstrumentAction() {}; + InstrumentAction(llvm::raw_fd_ostream *log, std::string const &pfx, bool citruninst) : + m_log(log), + m_pfx(pfx), + m_is_citruninst(citruninst) + {}; void EndSourceFileAction() override; std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance &, clang::StringRef) override; @@ -35,4 +40,25 @@ public: private: clang::Rewriter m_TheRewriter; RewriteASTConsumer *m_InstrumentASTConsumer; + llvm::raw_fd_ostream *m_log; + std::string m_pfx; + bool m_is_citruninst; +}; + +class InstrumentActionFactory : public clang::tooling::FrontendActionFactory { +public: + InstrumentActionFactory(llvm::raw_fd_ostream *log, std::string const &pfx, bool citruninst) : + m_log(log), + m_pfx(pfx), + m_is_citruninst(citruninst) + {}; + + clang::ASTFrontendAction *create() { + return new InstrumentAction(m_log, m_pfx, m_is_citruninst); + } + +private: + llvm::raw_fd_ostream *m_log; + std::string m_pfx; + bool m_is_citruninst; }; diff --git a/src/inst_ast_visitor.cc b/src/inst_ast_visitor.cc @@ -29,47 +29,68 @@ RewriteASTVisitor::VisitVarDecl(clang::VarDecl *d) bool RewriteASTVisitor::VisitStmt(clang::Stmt *s) { - clang::Stmt *stmt_to_inst = NULL; + m_totalstmt++; if (clang::isa<clang::IfStmt>(s)) { - stmt_to_inst = clang::cast<clang::IfStmt>(s)->getCond(); + s = clang::cast<clang::IfStmt>(s)->getCond(); + if (modify_stmt(s) == false) + return true; + m_ifstmt++; } else if (clang::isa<clang::ForStmt>(s)) { - stmt_to_inst = clang::cast<clang::ForStmt>(s)->getCond(); + s = clang::cast<clang::ForStmt>(s)->getCond(); + if (modify_stmt(s) == false) + return true; + m_forstmt++; } else if (clang::isa<clang::WhileStmt>(s)) { - stmt_to_inst = clang::cast<clang::WhileStmt>(s)->getCond(); + s = clang::cast<clang::WhileStmt>(s)->getCond(); + if (modify_stmt(s) == false) + return true; + m_whilestmt++; } else if (clang::isa<clang::SwitchStmt>(s)) { - stmt_to_inst = clang::cast<clang::SwitchStmt>(s)->getCond(); + s = clang::cast<clang::SwitchStmt>(s)->getCond(); + if (modify_stmt(s) == false) + return true; + m_switchstmt++; } else if (clang::isa<clang::ReturnStmt>(s)) { - stmt_to_inst = clang::cast<clang::ReturnStmt>(s)->getRetValue(); + s = clang::cast<clang::ReturnStmt>(s)->getRetValue(); + if (modify_stmt(s) == false) + return true; + m_returnstmt++; } /* else if (isa<BreakStmt>(s) || isa<ContinueStmt>(s) || || isa<SwitchCase>(s)) { } - */ else if (clang::isa<clang::DeclStmt>(s)) { } + */ else if (clang::isa<clang::CallExpr>(s)) { - stmt_to_inst = s; + if (modify_stmt(s) == false) + return true; + m_callexpr++; } - if (stmt_to_inst == NULL) - return true; + return true; +} + +bool +RewriteASTVisitor::modify_stmt(clang::Stmt *s) +{ + if (s == NULL) + return false; std::stringstream ss; ss << "(++_citrun_lines[" << m_SM.getPresumedLineNumber(s->getLocStart()) << "], "; - if (m_TheRewriter.InsertTextBefore(stmt_to_inst->getLocStart(), ss.str())) + if (m_TheRewriter.InsertTextBefore(s->getLocStart(), ss.str())) // writing failed, don't attempt to add ")" - return true; - - m_TheRewriter.InsertTextAfter(real_loc_end(stmt_to_inst), ")"); - ++m_rewrite_count; + return false; + m_TheRewriter.InsertTextAfter(real_loc_end(s), ")"); return true; } @@ -85,8 +106,10 @@ RewriteASTVisitor::VisitFunctionDecl(clang::FunctionDecl *f) // main() is a special case because it must start the runtime thread. clang::DeclarationName DeclName = f->getNameInfo().getName(); - if (DeclName.getAsString() == "main") + if (DeclName.getAsString() == "main") { + m_mainfunc++; rewrite_text << "citrun_start();"; + } clang::Stmt *FuncBody = f->getBody(); clang::SourceLocation curly_brace(FuncBody->getLocStart().getLocWithOffset(1)); @@ -100,6 +123,7 @@ RewriteASTVisitor::VisitFunctionDecl(clang::FunctionDecl *f) // Rewrite the function source right after the beginning curly brace. m_TheRewriter.InsertTextBefore(curly_brace, rewrite_text.str()); + m_funcdecl++; return true; } diff --git a/src/inst_ast_visitor.h b/src/inst_ast_visitor.h @@ -4,19 +4,39 @@ class RewriteASTVisitor : public clang::RecursiveASTVisitor<RewriteASTVisitor> { public: RewriteASTVisitor(clang::Rewriter &R) : + m_totalstmt(0), + m_funcdecl(0), + m_ifstmt(0), + m_forstmt(0), + m_whilestmt(0), + m_switchstmt(0), + m_returnstmt(0), + m_callexpr(0), + m_mainfunc(0), m_TheRewriter(R), - m_SM(R.getSourceMgr()), - m_rewrite_count(0) {} + m_SM(R.getSourceMgr()) + {} bool VisitVarDecl(clang::VarDecl *d); bool VisitStmt(clang::Stmt *s); bool VisitFunctionDecl(clang::FunctionDecl *f); - unsigned int GetRewriteCount() { return m_rewrite_count; }; + + unsigned int m_totalstmt; + unsigned int m_funcdecl; + unsigned int m_ifstmt; + unsigned int m_forstmt; + unsigned int m_whilestmt; + unsigned int m_switchstmt; + unsigned int m_returnstmt; + unsigned int m_callexpr; + unsigned int m_mainfunc; + private: + bool modify_stmt(clang::Stmt *); + clang::SourceLocation real_loc_end(clang::Stmt *); + clang::Rewriter &m_TheRewriter; clang::SourceManager &m_SM; clang::LangOptions m_lopt; - unsigned int m_rewrite_count; - clang::SourceLocation real_loc_end(clang::Stmt *s); }; diff --git a/src/inst_main.cc b/src/inst_main.cc @@ -300,7 +300,9 @@ CitrunInst::instrument() log->setPrefix(std::to_string(m_pid)); Tool.setDiagnosticConsumer(log); - if (Tool.run(clang::tooling::newFrontendActionFactory<InstrumentAction>().get())) { + std::unique_ptr<InstrumentActionFactory> f = + llvm::make_unique<InstrumentActionFactory>(&m_log, m_pfx, false); + if (Tool.run(f.get())) { m_log << m_pfx << "Instrumentation failed.\n"; return try_unmodified_compile(); }