citrun

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

commit a614fce0e51e0c64bbddfd7794fcd7f81d08870b
parent 9f24643c8b7425f5b9fb80dcc4b3e10c63401b68
Author: Kyle Milz <kyle@getaddrinfo.net>
Date:   Fri, 25 Mar 2016 11:35:49 -0600

instrument: shuffle some code around

Diffstat:
Minstrument/Makefile | 6+++---
Ainstrument/instrument_action.cc | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainstrument/instrument_action.h | 41+++++++++++++++++++++++++++++++++++++++++
Dinstrument/instrumenter.cc | 222-------------------------------------------------------------------------------
Dinstrument/instrumenter.h | 74--------------------------------------------------------------------------
Minstrument/main.cc | 4+---
Ainstrument/rewrite_ast_visitor.cc | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainstrument/rewrite_ast_visitor.h | 21+++++++++++++++++++++
8 files changed, 291 insertions(+), 302 deletions(-)

diff --git a/instrument/Makefile b/instrument/Makefile @@ -1,7 +1,7 @@ PROG = scv_instrument -SRCS = main.cc instrumenter.cc - -MKDEP = `llvm-config --cppflags` +SRCS = main.cc \ + instrument_action.cc \ + rewrite_ast_visitor.cc CXXFLAGS += -std=c++11 CXXFLAGS += `llvm-config --cxxflags` diff --git a/instrument/instrument_action.cc b/instrument/instrument_action.cc @@ -0,0 +1,132 @@ +#include <err.h> +#include <fcntl.h> // open +#include <limits.h> +#include <sys/stat.h> // mode flags +#include <unistd.h> // getcwd, access + +#include <fstream> +#include <iostream> +#include <sstream> +#include <string> + +#include <clang/Frontend/CompilerInstance.h> + +#include "instrument_action.h" +#include "runtime_h.h" + + +std::unique_ptr<ASTConsumer> +InstrumentAction::CreateASTConsumer(CompilerInstance &CI, StringRef file) +{ + // llvm::errs() << "** Creating AST consumer for: " << file << "\n"; + SourceManager &sm = CI.getSourceManager(); + TheRewriter.setSourceMgr(sm, CI.getLangOpts()); + + return std::unique_ptr<ASTConsumer>(new MyASTConsumer(TheRewriter)); +} + +unsigned int +read_src_number() +{ + char *cwd = getcwd(NULL, PATH_MAX); + if (cwd == NULL) + errx(1, "getcwd"); + + std::string src_number_filename(cwd); + src_number_filename.append("/SRC_NUMBER"); + + if (access(src_number_filename.c_str(), F_OK) == -1) { + // SRC_NUMBER does not exist, source number is 0 + return 0; + } + + // SRC_NUMBER exists, read its content + std::ifstream src_number_file; + unsigned int src_num = 0; + + src_number_file.open(src_number_filename, std::fstream::in); + src_number_file >> src_num; + src_number_file.close(); + + // Pre-increment. The current source number is the last one plus one + return ++src_num; +} + +void +write_src_number(int src_num) +{ + char *cwd = getcwd(NULL, PATH_MAX); + if (cwd == NULL) + errx(1, "getcwd"); + + std::string src_number_filename(cwd); + src_number_filename.append("/SRC_NUMBER"); + + std::ofstream src_number_file; + src_number_file.open(src_number_filename, std::fstream::out); + src_number_file << src_num; + src_number_file.close(); +} + +void +InstrumentAction::EndSourceFileAction() +{ + SourceManager &sm = TheRewriter.getSourceMgr(); + const FileID main_fid = sm.getMainFileID(); + // llvm::errs() << "** EndSourceFileAction for: " + // << sm.getFileEntryForID(main_fid)->getName() + // << "\n"; + + SourceLocation start = sm.getLocForStartOfFile(main_fid); + + SourceLocation end = sm.getLocForEndOfFile(main_fid); + unsigned int num_lines = sm.getPresumedLineNumber(end); + + std::string file_name = getCurrentFile(); + unsigned int tu_number = read_src_number(); + + std::stringstream ss; + // Embed the header directly in the primary source file. + ss << runtime_h << std::endl; + + // Define storage for coverage data + ss << "static uint64_t _scv_lines[" << num_lines << "];" << std::endl; + + // Always declare this. The next TU will overwrite this or there won't + // be a next TU. + ss << "struct _scv_node _scv_node" << tu_number + 1 << ";" << std::endl; + + // Define this translation units main book keeping data structure + ss << "struct _scv_node _scv_node" << tu_number << " = {" << std::endl + << " .lines_ptr = _scv_lines," << std::endl + << " .size = " << num_lines << "," << std::endl + << " .file_name = \"" << file_name << "\"," << std::endl + << " .next = &_scv_node" << tu_number + 1 << "," << std::endl + << "};" << std::endl; + + TheRewriter.InsertTextAfter(start, ss.str()); + + size_t last_slash = file_name.find_last_of('/'); + std::string base_dir(file_name.substr(0, last_slash + 1)); + base_dir.append("inst"); + + if (mkdir(base_dir.c_str(), S_IWUSR | S_IRUSR | S_IXUSR)) + if (errno != EEXIST) + // An error other than the directory existing occurred + err(1, "mkdir"); + + file_name.insert(last_slash + 1, "inst/"); + + // Instrumented source file might already exist + unlink(file_name.c_str()); + + int fd = open(file_name.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) + err(1, "open"); + llvm::raw_fd_ostream output(fd, /* close */ 1); + + // Write the instrumented source file + TheRewriter.getEditBuffer(main_fid).write(output); + + write_src_number(tu_number); +} diff --git a/instrument/instrument_action.h b/instrument/instrument_action.h @@ -0,0 +1,41 @@ +#include <clang/AST/ASTConsumer.h> +#include <clang/Frontend/FrontendActions.h> +#include <clang/Rewrite/Core/Rewriter.h> + +#include "rewrite_ast_visitor.h" + +using namespace clang; + +// For each source file provided to the tool, a new FrontendAction is created. +class InstrumentAction : public ASTFrontendAction { +public: + InstrumentAction() {}; + + void EndSourceFileAction() override; + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &, StringRef) override; + +private: + Rewriter TheRewriter; +}; + + +// Implementation of the ASTConsumer interface for reading an AST produced +// by the Clang parser. +class MyASTConsumer : public ASTConsumer { +public: + MyASTConsumer(Rewriter &R) : Visitor(R) {} + + // Override the method that gets called for each parsed top-level + // declaration. + bool HandleTopLevelDecl(DeclGroupRef DR) override { + for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) { + // Traverse the declaration using our AST visitor. + Visitor.TraverseDecl(*b); + // (*b)->dump(); + } + return true; + } + +private: + RewriteASTVisitor Visitor; +}; diff --git a/instrument/instrumenter.cc b/instrument/instrumenter.cc @@ -1,222 +0,0 @@ -#include <err.h> -#include <fcntl.h> // open -#include <limits.h> -#include <sys/stat.h> // mode flags -#include <unistd.h> // getcwd, access - -#include <fstream> -#include <iostream> -#include <sstream> -#include <string> - -#include <clang/AST/AST.h> -#include <clang/Lex/Lexer.h> -#include <clang/Frontend/CompilerInstance.h> - -#include "instrumenter.h" -#include "runtime_h.h" - -bool -instrumenter::VisitVarDecl(VarDecl *d) -{ - return true; -} - -bool -instrumenter::VisitStmt(Stmt *s) -{ - std::stringstream ss; - unsigned line = SM.getPresumedLineNumber(s->getLocStart()); - Stmt *stmt_to_inst = NULL; - - if (isa<IfStmt>(s)) { - stmt_to_inst = cast<IfStmt>(s)->getCond(); - } - else if (isa<ForStmt>(s)) { - stmt_to_inst = cast<ForStmt>(s)->getCond(); - } - else if (isa<WhileStmt>(s)) { - stmt_to_inst = cast<WhileStmt>(s)->getCond(); - } - else if (isa<SwitchStmt>(s)) { - stmt_to_inst = cast<SwitchStmt>(s)->getCond(); - } - else if (isa<ReturnStmt>(s)) { - stmt_to_inst = cast<ReturnStmt>(s)->getRetValue(); - } - /* - else if (isa<BreakStmt>(s) || isa<ContinueStmt>(s) || - || isa<SwitchCase>(s)) { - } - */ - else if (isa<DeclStmt>(s)) { - } - else if (isa<CallExpr>(s)) { - stmt_to_inst = s; - } - - if (stmt_to_inst == NULL) - return true; - - ss << "(++_scv_lines[" << line << "], "; - if (TheRewriter.InsertTextBefore(stmt_to_inst->getLocStart(), ss.str())) - // writing failed, don't attempt to add ")" - return true; - - TheRewriter.InsertTextAfter(real_loc_end(stmt_to_inst), ")"); - - return true; -} - -bool -instrumenter::VisitFunctionDecl(FunctionDecl *f) -{ - // Only function definitions (with bodies), not declarations. - if (f->hasBody() == 0) - return true; - - Stmt *FuncBody = f->getBody(); - - DeclarationName DeclName = f->getNameInfo().getName(); - std::string FuncName = DeclName.getAsString(); - - if (FuncName.compare("main") != 0) - // Function is not main - return true; - - std::stringstream ss; - // On some platforms we need to depend directly on a symbol provided by - // the runtime. Normally this isn't needed because the runtime only - // depends on symbols in the isntrumented application. - ss << "libscv_init();"; - SourceLocation curly_brace(FuncBody->getLocStart().getLocWithOffset(1)); - TheRewriter.InsertTextBefore(curly_brace, ss.str()); - - return true; -} - -SourceLocation -instrumenter::real_loc_end(Stmt *d) -{ - SourceLocation _e(d->getLocEnd()); - return SourceLocation(Lexer::getLocForEndOfToken(_e, 0, SM, lopt)); -} - -// MyFrontendAction --- - -ASTConsumer * -MyFrontendAction::CreateASTConsumer(CompilerInstance &CI, StringRef file) -{ - // llvm::errs() << "** Creating AST consumer for: " << file << "\n"; - SourceManager &sm = CI.getSourceManager(); - TheRewriter.setSourceMgr(sm, CI.getLangOpts()); - - return new MyASTConsumer(TheRewriter); -} - -unsigned int -read_src_number() -{ - char *cwd = getcwd(NULL, PATH_MAX); - if (cwd == NULL) - errx(1, "getcwd"); - - std::string src_number_filename(cwd); - src_number_filename.append("/SRC_NUMBER"); - - if (access(src_number_filename.c_str(), F_OK) == -1) { - // SRC_NUMBER does not exist, source number is 0 - return 0; - } - - // SRC_NUMBER exists, read its content - std::ifstream src_number_file; - unsigned int src_num = 0; - - src_number_file.open(src_number_filename, std::fstream::in); - src_number_file >> src_num; - src_number_file.close(); - - // Pre-increment. The current source number is the last one plus one - return ++src_num; -} - -void -write_src_number(int src_num) -{ - char *cwd = getcwd(NULL, PATH_MAX); - if (cwd == NULL) - errx(1, "getcwd"); - - std::string src_number_filename(cwd); - src_number_filename.append("/SRC_NUMBER"); - - std::ofstream src_number_file; - src_number_file.open(src_number_filename, std::fstream::out); - src_number_file << src_num; - src_number_file.close(); -} - -void -MyFrontendAction::EndSourceFileAction() -{ - SourceManager &sm = TheRewriter.getSourceMgr(); - const FileID main_fid = sm.getMainFileID(); - // llvm::errs() << "** EndSourceFileAction for: " - // << sm.getFileEntryForID(main_fid)->getName() - // << "\n"; - - SourceLocation start = sm.getLocForStartOfFile(main_fid); - - SourceLocation end = sm.getLocForEndOfFile(main_fid); - unsigned int num_lines = sm.getPresumedLineNumber(end); - - std::string file_name = getCurrentFile(); - unsigned int tu_number = read_src_number(); - - std::stringstream ss; - // Embed the header directly in the primary source file. - ss << runtime_h << std::endl; - - // Define storage for coverage data - ss << "static uint64_t _scv_lines[" << num_lines << "];" << std::endl; - - // Always declare this. The next TU will overwrite this or there won't - // be a next TU. - ss << "struct _scv_node _scv_node" << tu_number + 1 << ";" << std::endl; - - // Define this translation units main book keeping data structure - ss << "struct _scv_node _scv_node" << tu_number << " = {" << std::endl - << " .lines_ptr = _scv_lines," << std::endl - << " .size = " << num_lines << "," << std::endl - << " .file_name = \"" << file_name << "\"," << std::endl - << " .next = &_scv_node" << tu_number + 1 << "," << std::endl - << "};" << std::endl; - - TheRewriter.InsertTextAfter(start, ss.str()); - - size_t last_slash = file_name.find_last_of('/'); - std::string base_dir(file_name.substr(0, last_slash + 1)); - base_dir.append("inst"); - - if (mkdir(base_dir.c_str(), S_IWUSR | S_IRUSR | S_IXUSR)) - if (errno != EEXIST) - // An error other than the directory existing occurred - err(1, "mkdir"); - - file_name.insert(last_slash + 1, "inst/"); - - // Instrumented source file might already exist - unlink(file_name.c_str()); - - int fd = open(file_name.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); - if (fd < 0) - err(1, "open"); - llvm::raw_fd_ostream output(fd, /* close */ 1); - - // Write the instrumented source file - TheRewriter.getEditBuffer(main_fid).write(output); - - // If we got this far write the new translation unit number - write_src_number(tu_number); -} diff --git a/instrument/instrumenter.h b/instrument/instrumenter.h @@ -1,74 +0,0 @@ -#include <clang/AST/ASTConsumer.h> -#include <clang/AST/RecursiveASTVisitor.h> -#include <clang/Frontend/FrontendActions.h> -#include <clang/Rewrite/Core/Rewriter.h> - -using namespace clang; - - -// By implementing RecursiveASTVisitor, we can specify which AST nodes -// we're interested in by overriding relevant methods. -class instrumenter : public RecursiveASTVisitor<instrumenter> { -public: - instrumenter(Rewriter &R) : TheRewriter(R), SM(R.getSourceMgr()) {} - - - bool VisitVarDecl(VarDecl *d); - bool VisitStmt(Stmt *s); - bool VisitFunctionDecl(FunctionDecl *f); - -private: - Rewriter &TheRewriter; - SourceManager &SM; - LangOptions lopt; - - SourceLocation real_loc_end(Stmt *s); -}; - -// Implementation of the ASTConsumer interface for reading an AST produced -// by the Clang parser. -class MyASTConsumer : public ASTConsumer { -public: - MyASTConsumer(Rewriter &R) : Visitor(R) {} - - // Override the method that gets called for each parsed top-level - // declaration. - bool HandleTopLevelDecl(DeclGroupRef DR) override { - for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) { - // Traverse the declaration using our AST visitor. - Visitor.TraverseDecl(*b); - // (*b)->dump(); - } - return true; - } - -private: - instrumenter Visitor; -}; - -// For each source file provided to the tool, a new FrontendAction is created. -class MyFrontendAction : public ASTFrontendAction { -public: - MyFrontendAction() {}; - - void EndSourceFileAction() override; - ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef file); - -private: - Rewriter TheRewriter; -}; - - -#if 0 -class MFAF : public FrontendActionFactory { -public: - MFAF(std::vector<const char *> &i) : inst_files(i) {} - - FrontendAction *create() { - return new MyFrontendAction(); - } - -private: - std::vector<const char *> inst_files; -}; -#endif diff --git a/instrument/main.cc b/instrument/main.cc @@ -10,10 +10,8 @@ #include <clang/Tooling/CommonOptionsParser.h> #include <clang/Tooling/Tooling.h> -#include <clang/Rewrite/Core/Rewriter.h> -#include <llvm/Support/raw_ostream.h> -#include "instrumenter.h" +#include "instrument_action.h" using namespace clang; using namespace clang::tooling; diff --git a/instrument/rewrite_ast_visitor.cc b/instrument/rewrite_ast_visitor.cc @@ -0,0 +1,93 @@ +#include <sstream> +#include <string> + +#include <clang/AST/AST.h> +#include <clang/Lex/Lexer.h> + +#include "rewrite_ast_visitor.h" + +bool +RewriteASTVisitor::VisitVarDecl(VarDecl *d) +{ + return true; +} + +bool +RewriteASTVisitor::VisitStmt(Stmt *s) +{ + std::stringstream ss; + unsigned line = SM.getPresumedLineNumber(s->getLocStart()); + Stmt *stmt_to_inst = NULL; + + if (isa<IfStmt>(s)) { + stmt_to_inst = cast<IfStmt>(s)->getCond(); + } + else if (isa<ForStmt>(s)) { + stmt_to_inst = cast<ForStmt>(s)->getCond(); + } + else if (isa<WhileStmt>(s)) { + stmt_to_inst = cast<WhileStmt>(s)->getCond(); + } + else if (isa<SwitchStmt>(s)) { + stmt_to_inst = cast<SwitchStmt>(s)->getCond(); + } + else if (isa<ReturnStmt>(s)) { + stmt_to_inst = cast<ReturnStmt>(s)->getRetValue(); + } + /* + else if (isa<BreakStmt>(s) || isa<ContinueStmt>(s) || + || isa<SwitchCase>(s)) { + } + */ + else if (isa<DeclStmt>(s)) { + } + else if (isa<CallExpr>(s)) { + stmt_to_inst = s; + } + + if (stmt_to_inst == NULL) + return true; + + ss << "(++_scv_lines[" << line << "], "; + if (TheRewriter.InsertTextBefore(stmt_to_inst->getLocStart(), ss.str())) + // writing failed, don't attempt to add ")" + return true; + + TheRewriter.InsertTextAfter(real_loc_end(stmt_to_inst), ")"); + + return true; +} + +bool +RewriteASTVisitor::VisitFunctionDecl(FunctionDecl *f) +{ + // Only function definitions (with bodies), not declarations. + if (f->hasBody() == 0) + return true; + + Stmt *FuncBody = f->getBody(); + + DeclarationName DeclName = f->getNameInfo().getName(); + std::string FuncName = DeclName.getAsString(); + + if (FuncName.compare("main") != 0) + // Function is not main + return true; + + std::stringstream ss; + // On some platforms we need to depend directly on a symbol provided by + // the runtime. Normally this isn't needed because the runtime only + // depends on symbols in the isntrumented application. + ss << "libscv_init();"; + SourceLocation curly_brace(FuncBody->getLocStart().getLocWithOffset(1)); + TheRewriter.InsertTextBefore(curly_brace, ss.str()); + + return true; +} + +SourceLocation +RewriteASTVisitor::real_loc_end(Stmt *d) +{ + SourceLocation _e(d->getLocEnd()); + return SourceLocation(Lexer::getLocForEndOfToken(_e, 0, SM, lopt)); +} diff --git a/instrument/rewrite_ast_visitor.h b/instrument/rewrite_ast_visitor.h @@ -0,0 +1,21 @@ +#include <clang/AST/RecursiveASTVisitor.h> +#include <clang/Rewrite/Core/Rewriter.h> + +using namespace clang; + + +class RewriteASTVisitor : public RecursiveASTVisitor<RewriteASTVisitor> { +public: + RewriteASTVisitor(Rewriter &R) : TheRewriter(R), SM(R.getSourceMgr()) {} + + bool VisitVarDecl(VarDecl *d); + bool VisitStmt(Stmt *s); + bool VisitFunctionDecl(FunctionDecl *f); + +private: + Rewriter &TheRewriter; + SourceManager &SM; + LangOptions lopt; + + SourceLocation real_loc_end(Stmt *s); +};