commit bb5e8b942452ec5cd91e6780950fadd718396c2b
parent ca94c0dcbc1f6f8042a8dd540a287ffe0fe006d2
Author: Kyle Milz <kyle@getaddrinfo.net>
Date: Tue, 7 Jun 2016 22:57:44 -0600
src: rename source files instrument_* to inst_*
Diffstat:
10 files changed, 527 insertions(+), 527 deletions(-)
diff --git a/src/Jamfile b/src/Jamfile
@@ -1,9 +1,9 @@
SubDir TOP src ;
INSTRUMENT_SRCS =
- instrument_main.cc
- instrument_action.cc
- instrument_ast_visitor.cc ;
+ inst_main.cc
+ inst_action.cc
+ inst_ast_visitor.cc ;
CITRUN_SRCS =
gl_main.cc
diff --git a/src/inst_action.cc b/src/inst_action.cc
@@ -0,0 +1,143 @@
+#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 "inst_action.h"
+#include "runtime_h.h"
+
+
+#if LLVM_VER > 35
+std::unique_ptr<clang::ASTConsumer>
+#else
+clang::ASTConsumer *
+#endif
+InstrumentAction::CreateASTConsumer(clang::CompilerInstance &CI, clang::StringRef file)
+{
+ // llvm::errs() << "** Creating AST consumer for: " << file << "\n";
+ clang::SourceManager &sm = CI.getSourceManager();
+ TheRewriter.setSourceMgr(sm, CI.getLangOpts());
+
+ // Hang onto a reference to this so we can read from it later
+ InstrumentASTConsumer = new RewriteASTConsumer(TheRewriter);
+#if LLVM_VER > 35
+ return std::unique_ptr<clang::ASTConsumer>(InstrumentASTConsumer);
+#else
+ return InstrumentASTConsumer;
+#endif
+}
+
+std::string
+get_current_node(std::string file_path)
+{
+ size_t last_slash = file_path.find_last_of('/');
+ std::string fn(file_path.substr(last_slash + 1));
+
+ std::replace(fn.begin(), fn.end(), '.', '_');
+ std::replace(fn.begin(), fn.end(), '-', '_');
+
+ return fn;
+}
+
+std::string
+swap_last_node(std::string curr_node)
+{
+ char *cwd = getcwd(NULL, PATH_MAX);
+ if (cwd == NULL)
+ errx(1, "getcwd");
+
+ std::string src_number_filename(cwd);
+ src_number_filename.append("/LAST_NODE");
+
+ std::string last_node("NULL");
+
+ if (access(src_number_filename.c_str(), F_OK) == 0) {
+ // LAST_NODE exists, read last_node from file
+ std::ifstream src_number_file;
+ src_number_file.open(src_number_filename, std::fstream::in);
+ src_number_file >> last_node;
+ src_number_file.close();
+ }
+
+ // Always write curr_node to file
+ std::ofstream src_number_file;
+ src_number_file.open(src_number_filename, std::fstream::out);
+ src_number_file << curr_node;
+ src_number_file.close();
+
+ return last_node;
+}
+
+void
+InstrumentAction::EndSourceFileAction()
+{
+ clang::SourceManager &sm = TheRewriter.getSourceMgr();
+ const clang::FileID main_fid = sm.getMainFileID();
+ // llvm::errs() << "** EndSourceFileAction for: "
+ // << sm.getFileEntryForID(main_fid)->getName()
+ // << "\n";
+
+ clang::SourceLocation start = sm.getLocForStartOfFile(main_fid);
+ clang::SourceLocation end = sm.getLocForEndOfFile(main_fid);
+ unsigned int num_lines = sm.getPresumedLineNumber(end);
+
+ std::string file_name = getCurrentFile();
+ std::string curr_node = get_current_node(file_name);
+ std::string last_node = swap_last_node(curr_node);
+
+ //std::cerr << "LAST NODE = " << last_node << std::endl;
+
+ std::stringstream ss;
+ // Add preprocessor stuff so that the C runtime library links against
+ // C++ object code.
+ ss << "#ifdef __cplusplus" << std::endl;
+ ss << "extern \"C\" {" << std::endl;
+ ss << "#endif" << std::endl;
+
+ // Embed the header directly in the primary source file.
+ ss << runtime_h << std::endl;
+
+ // Define storage for coverage data
+ ss << "static uint64_t _citrun_lines[" << num_lines << "];" << std::endl;
+
+ // Get visitor instance to check how many times it rewrote something
+ RewriteASTVisitor visitor = InstrumentASTConsumer->get_visitor();
+
+ // Let the struct know this definition will be elsewhere
+ ss << "extern struct _citrun_node _citrun_node_" << last_node << ";" << std::endl;
+
+ // Define this translation units main book keeping data structure
+ ss << "struct _citrun_node _citrun_node_" << curr_node << " = {" << std::endl
+ << " .lines_ptr = _citrun_lines," << std::endl
+ << " .size = " << num_lines << "," << std::endl
+ << " .inst_sites = " << visitor.GetRewriteCount() << "," << std::endl
+ << " .file_name = \"" << file_name << "\"," << std::endl;
+ if (last_node.compare("NULL") == 0)
+ ss << " .next = NULL," << std::endl;
+ else
+ ss << " .next = &_citrun_node_" << last_node << "," << std::endl;
+ ss << "};" << std::endl;
+
+ // Close extern "C" {
+ ss << "#ifdef __cplusplus" << std::endl;
+ ss << "}" << std::endl;
+ ss << "#endif" << std::endl;
+
+ TheRewriter.InsertTextAfter(start, ss.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);
+}
diff --git a/src/inst_action.h b/src/inst_action.h
@@ -0,0 +1,42 @@
+#include <clang/AST/ASTConsumer.h>
+#include <clang/Frontend/FrontendActions.h>
+#include <clang/Rewrite/Core/Rewriter.h>
+
+#include "inst_ast_visitor.h"
+
+class RewriteASTConsumer : public clang::ASTConsumer {
+public:
+ RewriteASTConsumer(clang::Rewriter &R) : Visitor(R) {}
+
+ // Override the method that gets called for each parsed top-level
+ // declaration.
+ bool HandleTopLevelDecl(clang::DeclGroupRef DR) override {
+ for (auto &b : DR) {
+ // Traverse the declaration using our AST visitor.
+ Visitor.TraverseDecl(b);
+ // b->dump();
+ }
+ return true;
+ }
+
+ RewriteASTVisitor& get_visitor() { return Visitor; };
+private:
+ RewriteASTVisitor Visitor;
+};
+
+// For each source file provided to the tool, a new FrontendAction is created.
+class InstrumentAction : public clang::ASTFrontendAction {
+public:
+ InstrumentAction() {};
+
+ void EndSourceFileAction() override;
+#if LLVM_VER > 35
+ std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance &, clang::StringRef) override;
+#else
+ clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &, clang::StringRef) override;
+#endif
+
+private:
+ clang::Rewriter TheRewriter;
+ RewriteASTConsumer *InstrumentASTConsumer;
+};
diff --git a/src/inst_ast_visitor.cc b/src/inst_ast_visitor.cc
@@ -0,0 +1,94 @@
+#include <sstream>
+#include <string>
+
+#include <clang/AST/AST.h>
+#include <clang/Lex/Lexer.h>
+
+#include "inst_ast_visitor.h"
+
+bool
+RewriteASTVisitor::VisitVarDecl(clang::VarDecl *d)
+{
+ return true;
+}
+
+bool
+RewriteASTVisitor::VisitStmt(clang::Stmt *s)
+{
+ std::stringstream ss;
+ unsigned line = SM.getPresumedLineNumber(s->getLocStart());
+ clang::Stmt *stmt_to_inst = NULL;
+
+ if (clang::isa<clang::IfStmt>(s)) {
+ stmt_to_inst = clang::cast<clang::IfStmt>(s)->getCond();
+ }
+ else if (clang::isa<clang::ForStmt>(s)) {
+ stmt_to_inst = clang::cast<clang::ForStmt>(s)->getCond();
+ }
+ else if (clang::isa<clang::WhileStmt>(s)) {
+ stmt_to_inst = clang::cast<clang::WhileStmt>(s)->getCond();
+ }
+ else if (clang::isa<clang::SwitchStmt>(s)) {
+ stmt_to_inst = clang::cast<clang::SwitchStmt>(s)->getCond();
+ }
+ else if (clang::isa<clang::ReturnStmt>(s)) {
+ stmt_to_inst = clang::cast<clang::ReturnStmt>(s)->getRetValue();
+ }
+ /*
+ 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 (stmt_to_inst == NULL)
+ return true;
+
+ ss << "(++_citrun_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), ")");
+ ++rewrite_count;
+
+ return true;
+}
+
+bool
+RewriteASTVisitor::VisitFunctionDecl(clang::FunctionDecl *f)
+{
+ // Only function definitions (with bodies), not declarations.
+ if (f->hasBody() == 0)
+ return true;
+
+ clang::Stmt *FuncBody = f->getBody();
+
+ clang::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();";
+ clang::SourceLocation curly_brace(FuncBody->getLocStart().getLocWithOffset(1));
+ TheRewriter.InsertTextBefore(curly_brace, ss.str());
+
+ return true;
+}
+
+clang::SourceLocation
+RewriteASTVisitor::real_loc_end(clang::Stmt *d)
+{
+ clang::SourceLocation _e(d->getLocEnd());
+ return clang::SourceLocation(clang::Lexer::getLocForEndOfToken(_e, 0, SM, lopt));
+}
diff --git a/src/instrument_ast_visitor.h b/src/inst_ast_visitor.h
diff --git a/src/inst_main.cc b/src/inst_main.cc
@@ -0,0 +1,245 @@
+#include <err.h>
+#include <fcntl.h> // open
+#include <libgen.h>
+#include <string.h>
+#include <stdlib.h> // mkstemp, getenv
+#include <stdio.h> // tmpnam
+#include <unistd.h> // fork
+#ifdef __gnu_linux__
+ #include <bsd/stdlib.h> // setprogname
+#endif
+#include <sys/wait.h> // waitpid
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <clang/Tooling/CommonOptionsParser.h>
+#include <clang/Tooling/Tooling.h>
+
+#include "inst_action.h"
+
+#define STR_EXPAND(tok) #tok
+#define STR(tok) STR_EXPAND(tok)
+
+static llvm::cl::OptionCategory ToolingCategory("instrument options");
+
+void
+clean_path()
+{
+ char *scv_path = getenv("CITRUN_PATH");
+ char *path = getenv("PATH");
+ if (scv_path == NULL)
+ errx(1, "CITRUN_PATH not found in environment, not running "
+ "native compiler");
+ else if (path == NULL)
+ errx(1, "PATH not set, your build system needs to use "
+ "the PATH for this tool to be useful.");
+
+ // Filter CITRUN_PATH out of PATH
+ std::stringstream path_ss(path);
+ std::ostringstream new_path;
+ std::string component;
+ bool first_component = 1;
+
+ while (std::getline(path_ss, component, ':')) {
+ if (component.compare(scv_path) == 0)
+ continue;
+
+ if (first_component == 0)
+ new_path << ":";
+
+ // It wasn't $CITRUN_PATH, keep it
+ new_path << component;
+ first_component = 0;
+ }
+
+ // Set new $PATH
+ setenv("PATH", new_path.str().c_str(), 1);
+}
+
+int
+instrument(int argc, char *argv[], std::vector<std::string> const &source_files)
+{
+ if (source_files.size() == 0)
+ return 0;
+
+ std::vector<const char *> clang_argv;
+ clang_argv.push_back(argv[0]);
+ for (auto s : source_files)
+ clang_argv.push_back(s.c_str());
+
+ clang_argv.push_back("--");
+
+ // Append original command line verbatim
+ clang_argv.insert(clang_argv.end(), argv, argv + argc);
+
+ // When instrumenting certain code clang sometimes needs its own include
+ // files. These are defined globally per platform.
+ clang_argv.push_back(STR(CLANG_INCL));
+
+ // give clang it's <source files> -- <native command line> arg style
+ int clang_argc = clang_argv.size();
+ clang::tooling::CommonOptionsParser op(clang_argc, &clang_argv[0], ToolingCategory);
+ clang::tooling::ClangTool Tool(op.getCompilations(), op.getSourcePathList());
+
+ // ClangTool::run accepts a FrontendActionFactory, which is then used to
+ // create new objects implementing the FrontendAction interface. Here we
+ // use the helper newFrontendActionFactory to create a default factory
+ // that will return a new MyFrontendAction object every time. To
+ // further customize this, we could create our own factory class.
+ // int ret = Tool.run(new MFAF(inst_files));
+#if LLVM_VER > 34
+ int ret = Tool.run(&(*clang::tooling::newFrontendActionFactory<InstrumentAction>()));
+#else
+ int ret = Tool.run(clang::tooling::newFrontendActionFactory<InstrumentAction>());
+#endif
+ return ret;
+}
+
+bool
+ends_with(std::string const &value, std::string const &suffix)
+{
+ if (suffix.length() > value.length())
+ return false;
+
+ return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin());
+}
+
+void
+copy_file(std::string dst_fn, std::string src_fn)
+{
+ std::ifstream src(src_fn, std::ios::binary);
+ std::ofstream dst(dst_fn, std::ios::binary);
+
+ dst << src.rdbuf();
+}
+
+int
+main(int argc, char *argv[])
+{
+ // Set a better name than the symlink that was used to find this program
+ setprogname("citrun-inst");
+ clean_path();
+
+ std::vector<char *> args(argv, argv + argc);
+ std::vector<std::string> source_files;
+ std::map<std::string, std::string> temp_file_map;
+ // Keep track of some "well known" compiler flags for later.
+ bool object_arg = false;
+ bool compile_arg = false;
+
+ // Necessary because we're going to pass argv to execvp
+ argv[argc] = NULL;
+
+ for (auto &arg : args) {
+ if (strcmp(arg, "-E") == 0) {
+ // Preprocessing argument found, exec native command
+ if (execvp(argv[0], argv))
+ err(1, "execvp");
+ }
+ else if (strcmp(arg, "-o") == 0)
+ object_arg = true;
+ else if (strcmp(arg, "-c") == 0)
+ compile_arg = true;
+
+ // Find source files
+ if (ends_with(arg, ".c") || ends_with(arg, ".cc") ||
+ ends_with(arg, ".cpp") || ends_with(arg, ".cxx")) {
+
+ // Keep track of original source file names
+ source_files.push_back(arg);
+
+ if (getenv("CITRUN_LEAVE_MODIFIED_SRC"))
+ // Don't copy and restore original source files
+ continue;
+
+ char *dst_fn;
+ if ((dst_fn = tmpnam(NULL)) == NULL)
+ err(1, "tmpnam");
+
+ copy_file(dst_fn, arg);
+ temp_file_map[arg] = dst_fn;
+ }
+ }
+
+ if (instrument(argc, argv, source_files)) {
+ // If instrumentation failed, then modified source files were
+ // not written. So no need to replace them.
+ warnx("Instrumentation failed, running unmodified command.");
+ if (execvp(argv[0], argv))
+ err(1, "execvp");
+ }
+
+ bool linking = false;
+ if (!object_arg && !compile_arg && source_files.size() > 0)
+ // Assume single line a.out compilation
+ // $ gcc main.c
+ linking = true;
+ else if (object_arg && !compile_arg)
+ // gcc -o main main.o fib.o while.o
+ // gcc -o main main.c fib.c
+ linking = true;
+
+ std::string last_node_path("LAST_NODE");
+ if (linking) {
+ if (access(last_node_path.c_str(), F_OK)) {
+ // Couldn't access the LAST_NODE file, we cannot link
+ // to the runtime library without it.
+ warnx("LAST_NODE file not found.");
+ if (execvp(argv[0], argv))
+ err(1, "execvp");
+ }
+
+ std::ifstream last_node_ifstream;
+ std::string last_node;
+
+ last_node_ifstream.open(last_node_path, std::fstream::in);
+ last_node_ifstream >> last_node;
+ last_node_ifstream.close();
+
+ // We need to link the entry point in the runtime to the
+ // instrumented application. OS independent.
+ std::stringstream defsym_arg;
+#ifdef __APPLE__
+ defsym_arg << "-Wl,-alias,__citrun_node_";
+ defsym_arg << last_node;
+ defsym_arg << ",__citrun_tu_head";
+#else
+ defsym_arg << "-Wl,--defsym=_citrun_tu_head=_citrun_node_";
+ defsym_arg << last_node;
+#endif
+
+ // Add the runtime library and the symbol define hack
+ // automatically to the command line
+ args.push_back(strdup(defsym_arg.str().c_str()));
+ args.push_back(const_cast<char *>(STR(LIBCITRUN_PATH)));
+ }
+
+ // Instrumentation succeeded. Run the native compiler with a modified
+ // command line.
+ args.push_back(NULL);
+
+ pid_t child_pid;
+ if ((child_pid = fork()) < 0)
+ err(1, "fork");
+
+ if (child_pid == 0) {
+ // In child
+ if (execvp(args[0], &args[0]))
+ err(1, "execvp");
+ }
+
+ int status;
+ if (waitpid(child_pid, &status, 0) < 0)
+ err(1, "waitpid");
+
+ for (auto &tmp_file : temp_file_map) {
+ copy_file(tmp_file.first, tmp_file.second);
+ unlink(tmp_file.second.c_str());
+ }
+
+ if (linking)
+ unlink(last_node_path.c_str());
+}
diff --git a/src/instrument_action.cc b/src/instrument_action.cc
@@ -1,143 +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/Frontend/CompilerInstance.h>
-
-#include "instrument_action.h"
-#include "runtime_h.h"
-
-
-#if LLVM_VER > 35
-std::unique_ptr<clang::ASTConsumer>
-#else
-clang::ASTConsumer *
-#endif
-InstrumentAction::CreateASTConsumer(clang::CompilerInstance &CI, clang::StringRef file)
-{
- // llvm::errs() << "** Creating AST consumer for: " << file << "\n";
- clang::SourceManager &sm = CI.getSourceManager();
- TheRewriter.setSourceMgr(sm, CI.getLangOpts());
-
- // Hang onto a reference to this so we can read from it later
- InstrumentASTConsumer = new RewriteASTConsumer(TheRewriter);
-#if LLVM_VER > 35
- return std::unique_ptr<clang::ASTConsumer>(InstrumentASTConsumer);
-#else
- return InstrumentASTConsumer;
-#endif
-}
-
-std::string
-get_current_node(std::string file_path)
-{
- size_t last_slash = file_path.find_last_of('/');
- std::string fn(file_path.substr(last_slash + 1));
-
- std::replace(fn.begin(), fn.end(), '.', '_');
- std::replace(fn.begin(), fn.end(), '-', '_');
-
- return fn;
-}
-
-std::string
-swap_last_node(std::string curr_node)
-{
- char *cwd = getcwd(NULL, PATH_MAX);
- if (cwd == NULL)
- errx(1, "getcwd");
-
- std::string src_number_filename(cwd);
- src_number_filename.append("/LAST_NODE");
-
- std::string last_node("NULL");
-
- if (access(src_number_filename.c_str(), F_OK) == 0) {
- // LAST_NODE exists, read last_node from file
- std::ifstream src_number_file;
- src_number_file.open(src_number_filename, std::fstream::in);
- src_number_file >> last_node;
- src_number_file.close();
- }
-
- // Always write curr_node to file
- std::ofstream src_number_file;
- src_number_file.open(src_number_filename, std::fstream::out);
- src_number_file << curr_node;
- src_number_file.close();
-
- return last_node;
-}
-
-void
-InstrumentAction::EndSourceFileAction()
-{
- clang::SourceManager &sm = TheRewriter.getSourceMgr();
- const clang::FileID main_fid = sm.getMainFileID();
- // llvm::errs() << "** EndSourceFileAction for: "
- // << sm.getFileEntryForID(main_fid)->getName()
- // << "\n";
-
- clang::SourceLocation start = sm.getLocForStartOfFile(main_fid);
- clang::SourceLocation end = sm.getLocForEndOfFile(main_fid);
- unsigned int num_lines = sm.getPresumedLineNumber(end);
-
- std::string file_name = getCurrentFile();
- std::string curr_node = get_current_node(file_name);
- std::string last_node = swap_last_node(curr_node);
-
- //std::cerr << "LAST NODE = " << last_node << std::endl;
-
- std::stringstream ss;
- // Add preprocessor stuff so that the C runtime library links against
- // C++ object code.
- ss << "#ifdef __cplusplus" << std::endl;
- ss << "extern \"C\" {" << std::endl;
- ss << "#endif" << std::endl;
-
- // Embed the header directly in the primary source file.
- ss << runtime_h << std::endl;
-
- // Define storage for coverage data
- ss << "static uint64_t _citrun_lines[" << num_lines << "];" << std::endl;
-
- // Get visitor instance to check how many times it rewrote something
- RewriteASTVisitor visitor = InstrumentASTConsumer->get_visitor();
-
- // Let the struct know this definition will be elsewhere
- ss << "extern struct _citrun_node _citrun_node_" << last_node << ";" << std::endl;
-
- // Define this translation units main book keeping data structure
- ss << "struct _citrun_node _citrun_node_" << curr_node << " = {" << std::endl
- << " .lines_ptr = _citrun_lines," << std::endl
- << " .size = " << num_lines << "," << std::endl
- << " .inst_sites = " << visitor.GetRewriteCount() << "," << std::endl
- << " .file_name = \"" << file_name << "\"," << std::endl;
- if (last_node.compare("NULL") == 0)
- ss << " .next = NULL," << std::endl;
- else
- ss << " .next = &_citrun_node_" << last_node << "," << std::endl;
- ss << "};" << std::endl;
-
- // Close extern "C" {
- ss << "#ifdef __cplusplus" << std::endl;
- ss << "}" << std::endl;
- ss << "#endif" << std::endl;
-
- TheRewriter.InsertTextAfter(start, ss.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);
-}
diff --git a/src/instrument_action.h b/src/instrument_action.h
@@ -1,42 +0,0 @@
-#include <clang/AST/ASTConsumer.h>
-#include <clang/Frontend/FrontendActions.h>
-#include <clang/Rewrite/Core/Rewriter.h>
-
-#include "instrument_ast_visitor.h"
-
-class RewriteASTConsumer : public clang::ASTConsumer {
-public:
- RewriteASTConsumer(clang::Rewriter &R) : Visitor(R) {}
-
- // Override the method that gets called for each parsed top-level
- // declaration.
- bool HandleTopLevelDecl(clang::DeclGroupRef DR) override {
- for (auto &b : DR) {
- // Traverse the declaration using our AST visitor.
- Visitor.TraverseDecl(b);
- // b->dump();
- }
- return true;
- }
-
- RewriteASTVisitor& get_visitor() { return Visitor; };
-private:
- RewriteASTVisitor Visitor;
-};
-
-// For each source file provided to the tool, a new FrontendAction is created.
-class InstrumentAction : public clang::ASTFrontendAction {
-public:
- InstrumentAction() {};
-
- void EndSourceFileAction() override;
-#if LLVM_VER > 35
- std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance &, clang::StringRef) override;
-#else
- clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &, clang::StringRef) override;
-#endif
-
-private:
- clang::Rewriter TheRewriter;
- RewriteASTConsumer *InstrumentASTConsumer;
-};
diff --git a/src/instrument_ast_visitor.cc b/src/instrument_ast_visitor.cc
@@ -1,94 +0,0 @@
-#include <sstream>
-#include <string>
-
-#include <clang/AST/AST.h>
-#include <clang/Lex/Lexer.h>
-
-#include "instrument_ast_visitor.h"
-
-bool
-RewriteASTVisitor::VisitVarDecl(clang::VarDecl *d)
-{
- return true;
-}
-
-bool
-RewriteASTVisitor::VisitStmt(clang::Stmt *s)
-{
- std::stringstream ss;
- unsigned line = SM.getPresumedLineNumber(s->getLocStart());
- clang::Stmt *stmt_to_inst = NULL;
-
- if (clang::isa<clang::IfStmt>(s)) {
- stmt_to_inst = clang::cast<clang::IfStmt>(s)->getCond();
- }
- else if (clang::isa<clang::ForStmt>(s)) {
- stmt_to_inst = clang::cast<clang::ForStmt>(s)->getCond();
- }
- else if (clang::isa<clang::WhileStmt>(s)) {
- stmt_to_inst = clang::cast<clang::WhileStmt>(s)->getCond();
- }
- else if (clang::isa<clang::SwitchStmt>(s)) {
- stmt_to_inst = clang::cast<clang::SwitchStmt>(s)->getCond();
- }
- else if (clang::isa<clang::ReturnStmt>(s)) {
- stmt_to_inst = clang::cast<clang::ReturnStmt>(s)->getRetValue();
- }
- /*
- 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 (stmt_to_inst == NULL)
- return true;
-
- ss << "(++_citrun_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), ")");
- ++rewrite_count;
-
- return true;
-}
-
-bool
-RewriteASTVisitor::VisitFunctionDecl(clang::FunctionDecl *f)
-{
- // Only function definitions (with bodies), not declarations.
- if (f->hasBody() == 0)
- return true;
-
- clang::Stmt *FuncBody = f->getBody();
-
- clang::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();";
- clang::SourceLocation curly_brace(FuncBody->getLocStart().getLocWithOffset(1));
- TheRewriter.InsertTextBefore(curly_brace, ss.str());
-
- return true;
-}
-
-clang::SourceLocation
-RewriteASTVisitor::real_loc_end(clang::Stmt *d)
-{
- clang::SourceLocation _e(d->getLocEnd());
- return clang::SourceLocation(clang::Lexer::getLocForEndOfToken(_e, 0, SM, lopt));
-}
diff --git a/src/instrument_main.cc b/src/instrument_main.cc
@@ -1,245 +0,0 @@
-#include <err.h>
-#include <fcntl.h> // open
-#include <libgen.h>
-#include <string.h>
-#include <stdlib.h> // mkstemp, getenv
-#include <stdio.h> // tmpnam
-#include <unistd.h> // fork
-#ifdef __gnu_linux__
- #include <bsd/stdlib.h> // setprogname
-#endif
-#include <sys/wait.h> // waitpid
-
-#include <fstream>
-#include <iostream>
-#include <sstream>
-#include <string>
-
-#include <clang/Tooling/CommonOptionsParser.h>
-#include <clang/Tooling/Tooling.h>
-
-#include "instrument_action.h"
-
-#define STR_EXPAND(tok) #tok
-#define STR(tok) STR_EXPAND(tok)
-
-static llvm::cl::OptionCategory ToolingCategory("instrument options");
-
-void
-clean_path()
-{
- char *scv_path = getenv("CITRUN_PATH");
- char *path = getenv("PATH");
- if (scv_path == NULL)
- errx(1, "CITRUN_PATH not found in environment, not running "
- "native compiler");
- else if (path == NULL)
- errx(1, "PATH not set, your build system needs to use "
- "the PATH for this tool to be useful.");
-
- // Filter CITRUN_PATH out of PATH
- std::stringstream path_ss(path);
- std::ostringstream new_path;
- std::string component;
- bool first_component = 1;
-
- while (std::getline(path_ss, component, ':')) {
- if (component.compare(scv_path) == 0)
- continue;
-
- if (first_component == 0)
- new_path << ":";
-
- // It wasn't $CITRUN_PATH, keep it
- new_path << component;
- first_component = 0;
- }
-
- // Set new $PATH
- setenv("PATH", new_path.str().c_str(), 1);
-}
-
-int
-instrument(int argc, char *argv[], std::vector<std::string> const &source_files)
-{
- if (source_files.size() == 0)
- return 0;
-
- std::vector<const char *> clang_argv;
- clang_argv.push_back(argv[0]);
- for (auto s : source_files)
- clang_argv.push_back(s.c_str());
-
- clang_argv.push_back("--");
-
- // Append original command line verbatim
- clang_argv.insert(clang_argv.end(), argv, argv + argc);
-
- // When instrumenting certain code clang sometimes needs its own include
- // files. These are defined globally per platform.
- clang_argv.push_back(STR(CLANG_INCL));
-
- // give clang it's <source files> -- <native command line> arg style
- int clang_argc = clang_argv.size();
- clang::tooling::CommonOptionsParser op(clang_argc, &clang_argv[0], ToolingCategory);
- clang::tooling::ClangTool Tool(op.getCompilations(), op.getSourcePathList());
-
- // ClangTool::run accepts a FrontendActionFactory, which is then used to
- // create new objects implementing the FrontendAction interface. Here we
- // use the helper newFrontendActionFactory to create a default factory
- // that will return a new MyFrontendAction object every time. To
- // further customize this, we could create our own factory class.
- // int ret = Tool.run(new MFAF(inst_files));
-#if LLVM_VER > 34
- int ret = Tool.run(&(*clang::tooling::newFrontendActionFactory<InstrumentAction>()));
-#else
- int ret = Tool.run(clang::tooling::newFrontendActionFactory<InstrumentAction>());
-#endif
- return ret;
-}
-
-bool
-ends_with(std::string const &value, std::string const &suffix)
-{
- if (suffix.length() > value.length())
- return false;
-
- return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin());
-}
-
-void
-copy_file(std::string dst_fn, std::string src_fn)
-{
- std::ifstream src(src_fn, std::ios::binary);
- std::ofstream dst(dst_fn, std::ios::binary);
-
- dst << src.rdbuf();
-}
-
-int
-main(int argc, char *argv[])
-{
- // Set a better name than the symlink that was used to find this program
- setprogname("citrun-inst");
- clean_path();
-
- std::vector<char *> args(argv, argv + argc);
- std::vector<std::string> source_files;
- std::map<std::string, std::string> temp_file_map;
- // Keep track of some "well known" compiler flags for later.
- bool object_arg = false;
- bool compile_arg = false;
-
- // Necessary because we're going to pass argv to execvp
- argv[argc] = NULL;
-
- for (auto &arg : args) {
- if (strcmp(arg, "-E") == 0) {
- // Preprocessing argument found, exec native command
- if (execvp(argv[0], argv))
- err(1, "execvp");
- }
- else if (strcmp(arg, "-o") == 0)
- object_arg = true;
- else if (strcmp(arg, "-c") == 0)
- compile_arg = true;
-
- // Find source files
- if (ends_with(arg, ".c") || ends_with(arg, ".cc") ||
- ends_with(arg, ".cpp") || ends_with(arg, ".cxx")) {
-
- // Keep track of original source file names
- source_files.push_back(arg);
-
- if (getenv("CITRUN_LEAVE_MODIFIED_SRC"))
- // Don't copy and restore original source files
- continue;
-
- char *dst_fn;
- if ((dst_fn = tmpnam(NULL)) == NULL)
- err(1, "tmpnam");
-
- copy_file(dst_fn, arg);
- temp_file_map[arg] = dst_fn;
- }
- }
-
- if (instrument(argc, argv, source_files)) {
- // If instrumentation failed, then modified source files were
- // not written. So no need to replace them.
- warnx("Instrumentation failed, running unmodified command.");
- if (execvp(argv[0], argv))
- err(1, "execvp");
- }
-
- bool linking = false;
- if (!object_arg && !compile_arg && source_files.size() > 0)
- // Assume single line a.out compilation
- // $ gcc main.c
- linking = true;
- else if (object_arg && !compile_arg)
- // gcc -o main main.o fib.o while.o
- // gcc -o main main.c fib.c
- linking = true;
-
- std::string last_node_path("LAST_NODE");
- if (linking) {
- if (access(last_node_path.c_str(), F_OK)) {
- // Couldn't access the LAST_NODE file, we cannot link
- // to the runtime library without it.
- warnx("LAST_NODE file not found.");
- if (execvp(argv[0], argv))
- err(1, "execvp");
- }
-
- std::ifstream last_node_ifstream;
- std::string last_node;
-
- last_node_ifstream.open(last_node_path, std::fstream::in);
- last_node_ifstream >> last_node;
- last_node_ifstream.close();
-
- // We need to link the entry point in the runtime to the
- // instrumented application. OS independent.
- std::stringstream defsym_arg;
-#ifdef __APPLE__
- defsym_arg << "-Wl,-alias,__citrun_node_";
- defsym_arg << last_node;
- defsym_arg << ",__citrun_tu_head";
-#else
- defsym_arg << "-Wl,--defsym=_citrun_tu_head=_citrun_node_";
- defsym_arg << last_node;
-#endif
-
- // Add the runtime library and the symbol define hack
- // automatically to the command line
- args.push_back(strdup(defsym_arg.str().c_str()));
- args.push_back(const_cast<char *>(STR(LIBCITRUN_PATH)));
- }
-
- // Instrumentation succeeded. Run the native compiler with a modified
- // command line.
- args.push_back(NULL);
-
- pid_t child_pid;
- if ((child_pid = fork()) < 0)
- err(1, "fork");
-
- if (child_pid == 0) {
- // In child
- if (execvp(args[0], &args[0]))
- err(1, "execvp");
- }
-
- int status;
- if (waitpid(child_pid, &status, 0) < 0)
- err(1, "waitpid");
-
- for (auto &tmp_file : temp_file_map) {
- copy_file(tmp_file.first, tmp_file.second);
- unlink(tmp_file.second.c_str());
- }
-
- if (linking)
- unlink(last_node_path.c_str());
-}