commit a87b2c48b669f89921b53219c2f30cf808c00765
parent a054b0267b032d6645d0a8137487b1abfc2e73f2
Author: kyle <kyle@getaddrinfo.net>
Date: Sat, 31 Oct 2015 13:33:52 -0600
instrument: look like a compiler
- make instrument accept a native compiler command line
- idea here is to use PATH to call instrument before native compiler
- and then instrument exec()'s the native compiler after it's done
- tricky bit was getting instrument out of the PATH so execvp doesn't loop
Diffstat:
2 files changed, 118 insertions(+), 9 deletions(-)
diff --git a/instrument/Makefile b/instrument/Makefile
@@ -1,7 +1,8 @@
CXXFLAGS += -I/usr/local/include
CXXFLAGS += -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS
-CXXFLAGS += -fno-rtti -g
-CXX = eg++ -std=c++1y
+CXXFLAGS += -fno-rtti
+# CXXFLAGS += -ggdb -DDEBUG
+CXX = eg++ -std=c++11
# clang has a circular dependencies here, use "linker group options" to avoid
LDLIBS += \
diff --git a/instrument/instrument.cpp b/instrument/instrument.cpp
@@ -8,6 +8,9 @@
// Eli Bendersky (eliben@gmail.com)
// This code is in the public domain
//------------------------------------------------------------------------------
+#include <err.h>
+#include <unistd.h>
+
#include <sstream>
#include <string>
#include <iostream>
@@ -28,7 +31,7 @@ using namespace clang;
using namespace clang::driver;
using namespace clang::tooling;
-static llvm::cl::OptionCategory ToolingSampleCategory("Tooling Sample");
+static llvm::cl::OptionCategory ToolingCategory("instrument options");
// By implementing RecursiveASTVisitor, we can specify which AST nodes
@@ -62,7 +65,7 @@ instrumenter::VisitStmt(Stmt *s)
{
std::stringstream ss;
unsigned line = SM.getPresumedLineNumber(s->getLocStart());
- Stmt *stmt_to_inst;
+ Stmt *stmt_to_inst = NULL;
if (isa<IfStmt>(s)) {
IfStmt *IfStatement = cast<IfStmt>(s);
@@ -90,7 +93,8 @@ instrumenter::VisitStmt(Stmt *s)
else if (isa<CallExpr>(s)) {
stmt_to_inst = s;
}
- else
+
+ if (stmt_to_inst == NULL)
return true;
ss << "(lines[" << line << "] = 1, ";
@@ -176,7 +180,7 @@ public:
SourceLocation start = sm.getLocForStartOfFile(main_fid);
std::stringstream ss;
- // This isn't the number of lines but rather bytes
+ // Add declarations for coverage buffers
int file_bytes = sm.getFileIDSize(main_fid);
ss << "unsigned int lines[" << file_bytes << "];"
<< std::endl;
@@ -200,10 +204,101 @@ private:
Rewriter TheRewriter;
};
+void
+clean_path()
+{
+ // remove SCV_PATH from PATH
+
+ char *scv_path = getenv("SCV_PATH");
+ char *path = getenv("PATH");
+ if (scv_path == NULL)
+ errx(1, "SCV_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.");
+#ifdef DEBUG
+ std::cout << "SCV_PATH=" << scv_path << std::endl;
+ std::cout << "old PATH=" << path << std::endl;
+#endif
+
+ int new_path_pos = 0;
+ char *new_path = (char *)calloc(strlen(path), 1);
+
+ char *tok = strtok(path, ":");
+ bool wrote_first_token = false;
+ while (tok != NULL) {
+ if (strncmp(scv_path, tok, 1024) != 0) {
+ if (wrote_first_token == true) {
+ strcat(new_path + new_path_pos, ":");
+ new_path_pos++;
+ }
+ strcat(new_path + new_path_pos, tok);
+ new_path_pos += strlen(tok);
+ wrote_first_token = true;
+ }
+ tok = strtok(NULL, ":");
+ }
+
+ setenv("PATH", new_path, 1);
+#ifdef DEBUG
+ std::cout << "new " << new_path << std::endl;
+ system("env");
+#endif
+}
+
int
-main(int argc, const char *argv[])
+main(int argc, char *argv[])
{
- CommonOptionsParser op(argc, argv, ToolingSampleCategory);
+ std::vector<const char *> source_files;
+ char *exec_argv[argc + 1];
+
+ for (int i = 0; i < argc; i++) {
+ exec_argv[i] = strdup(argv[i]);
+
+ int arg_len = strlen(argv[i]);
+ if (arg_len < 4)
+ continue;
+
+ // compare last four bytes of argument
+ if (strcmp(argv[i] + arg_len - 4, ".cpp") == 0 ||
+ strcmp(argv[i] + arg_len - 2, ".c") == 0)
+ source_files.push_back(argv[i]);
+ }
+ // very important that argv passed to execvp is NULL terminated
+ exec_argv[argc] = NULL;
+
+ // run native command if there's no source files to instrument
+ if (source_files.size() == 0) {
+ warnx("no source files found on command line");
+ clean_path();
+
+ if (execvp(exec_argv[0], exec_argv))
+ err(1, "execvp");
+ }
+
+ const char *clang_argv[source_files.size() + 1 + argc];
+ int clang_argc = 0;
+
+ clang_argv[clang_argc++] = argv[0];
+ for (auto s : source_files)
+ clang_argv[clang_argc++] = s;
+
+ clang_argv[clang_argc++] = "--";
+
+ // append original command line verbatim
+ for (int i = 0; i < argc; i++)
+ clang_argv[clang_argc++] = argv[i];
+
+ // print out
+#ifdef DEBUG
+ for (int i = 0; i < clang_argc; i++)
+ std::cout << clang_argv[i] << " ";
+ std::cout << std::endl;
+#endif
+
+ // give clang it's <source files> -- <native command line> arg style
+ CommonOptionsParser op(clang_argc, clang_argv, ToolingCategory);
ClangTool Tool(op.getCompilations(), op.getSourcePathList());
// ClangTool::run accepts a FrontendActionFactory, which is then used to
@@ -211,5 +306,18 @@ main(int argc, const char *argv[])
// 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.
- return Tool.run(newFrontendActionFactory<MyFrontendAction>());
+ int ret = Tool.run(newFrontendActionFactory<MyFrontendAction>());
+ if (ret) {
+ std::cout << "Instrumentation failed" << std::endl;
+ return ret;
+ }
+
+ clean_path();
+#if DEBUG
+ std::cout << "Calling real compiler " << exec_argv[0] << std::endl;
+#endif
+
+ if (execvp(exec_argv[0], exec_argv))
+ err(1, "execvp");
+ std::cout << "here" << std::endl;
}