citrun

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

commit 206aa1a24869b9c69c5e79643d395fe84ec30984
parent fddfdf016caa1d7214037ecb3cd3fc06051bba8c
Author: kyle <kyle@getaddrinfo.net>
Date:   Sun,  1 Nov 2015 14:29:55 -0700

instrument: rewrite original source file

- first, save original source file by adding .backup suffix
- then rewrite original source file with instrumented copy
- then call original compiler with original command line
  - this is needed because object file name is based on source file name
- then, restore the original source files after everything is done

Diffstat:
Minstrument/Makefile | 4++++
Minstrument/main.cpp | 110++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mtests/fibonacci/Makefile | 12+++++++++++-
Rtests/fibonacci/prog.c -> tests/fibonacci/fib.c | 0
Rtests/fibonacci/instrumented.c -> tests/fibonacci/fib_reference.c | 0
5 files changed, 82 insertions(+), 44 deletions(-)

diff --git a/instrument/Makefile b/instrument/Makefile @@ -1,3 +1,7 @@ +.ifdef SCV_PATH +error_SCV_PATH +.endif + CXXFLAGS += -I/usr/local/include CXXFLAGS += -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS CXXFLAGS += -fno-rtti diff --git a/instrument/main.cpp b/instrument/main.cpp @@ -3,7 +3,9 @@ #include <stdlib.h> // mktemp #include <unistd.h> #include <sys/stat.h> // mode flags +#include <sys/wait.h> // wait +#include <fstream> #include <iostream> #include <sstream> #include <string> @@ -26,7 +28,8 @@ static llvm::cl::OptionCategory ToolingCategory("instrument options"); // For each source file provided to the tool, a new FrontendAction is created. class MyFrontendAction : public ASTFrontendAction { public: - MyFrontendAction(std::vector<const char *> &); + MyFrontendAction() {}; + void EndSourceFileAction() override { SourceManager &sm = TheRewriter.getSourceMgr(); const FileID main_fid = sm.getMainFileID(); @@ -35,6 +38,7 @@ public: // << "\n"; SourceLocation start = sm.getLocForStartOfFile(main_fid); + std::string file_name = getCurrentFile(); std::stringstream ss; // Add declarations for coverage buffers @@ -42,11 +46,11 @@ public: ss << "unsigned int lines[" << file_bytes << "];" << std::endl; ss << "int size = " << file_bytes << ";" << std::endl; + ss << "char file_name[] = \"" << file_name << "\";" << std::endl; TheRewriter.InsertTextAfter(start, ss.str()); - // Now emit the rewritten buffer. - int fd = open(inst_files[0], O_WRONLY | O_CREAT, - S_IRUSR | S_IWUSR); + // rewrite the original source file + int fd = open(file_name.c_str(), O_WRONLY | O_CREAT); if (fd < 0) err(1, "open"); llvm::raw_fd_ostream output(fd, /* close */ 1); @@ -65,21 +69,14 @@ public: private: Rewriter TheRewriter; - - std::vector<const char *> inst_files; }; -MyFrontendAction::MyFrontendAction(std::vector<const char *> &i) : - inst_files(i) -{ -} - class MFAF : public FrontendActionFactory { public: MFAF(std::vector<const char *> &i) : inst_files(i) {} FrontendAction *create() { - return new MyFrontendAction(inst_files); + return new MyFrontendAction(); } private: @@ -131,8 +128,7 @@ clean_path() } void -instrument(int argc, char *argv[], std::vector<const char *> &source_files, - std::vector<const char *> &inst_files) +instrument(int argc, char *argv[], std::vector<const char *> &source_files) { const char *clang_argv[source_files.size() + 1 + argc]; int clang_argc = 0; @@ -163,8 +159,8 @@ instrument(int argc, char *argv[], std::vector<const char *> &source_files, // 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)); - // int ret = Tool.run(newFrontendActionFactory<MyFrontendAction>()); + // int ret = Tool.run(new MFAF(inst_files)); + int ret = Tool.run(newFrontendActionFactory<MyFrontendAction>()); if (ret) errx(1, "Instrumentation failed"); } @@ -173,56 +169,84 @@ int main(int argc, char *argv[]) { std::vector<const char *> source_files; - std::vector<const char *> inst_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) { + strcmp(argv[i] + arg_len - 2, ".c") == 0) // keep track of original source file names source_files.push_back(argv[i]); - - char *inst_filename = (char *)calloc(PATH_MAX, 1); - if (inst_filename == NULL) - err(1, "calloc"); - - strncpy(inst_filename, argv[i], arg_len - 2); - strcat(inst_filename, "_inst.c"); - - // source code rewriter needs to know this file - inst_files.push_back(inst_filename); - // native compiler uses this source file instead - exec_argv[i] = inst_filename; - } } // very important that argv passed to execvp is NULL terminated - exec_argv[argc] = NULL; + argv[argc] = NULL; // run native command if there's no source files to instrument if (source_files.size() == 0) { +#if DEBUG warnx("no source files found on command line"); - +#endif clean_path(); - if (execvp(exec_argv[0], exec_argv)) + if (execvp(argv[0], argv)) err(1, "execvp"); } + // backup original source files + for (auto s : source_files) { + std::ifstream src(s, std::ios::binary); + std::string dst_fn(s); + std::ofstream dst(dst_fn.append(".backup"), std::ios::binary); + + dst << src.rdbuf(); + + src.close(); + dst.close(); + } + // run instrumentation on detected source files - instrument(argc, argv, source_files, inst_files); + instrument(argc, argv, source_files); #if DEBUG - std::cout << "Calling real compiler " << exec_argv[0] << std::endl; + std::cout << "Calling real compiler" << std::endl; #endif - - // exec native compiler with instrumented source files clean_path(); - if (execvp(exec_argv[0], exec_argv)) - err(1, "execvp"); + + pid_t pid = fork(); + if (pid == 0) { + // child, exec native compiler with instrumented source files + if (execvp(argv[0], argv)) + err(1, "execvp"); + } + else if (pid > 0) { + // parent + int status = 1; + int ret; + while (ret = wait(&status)) { + if (ret != -1) + break; + if (errno == EINTR) + continue; + // something bad happened + err(1, "wait"); + } +#ifdef DEBUG + std::cout << "parent: restoring files" << std::endl; +#endif + // restore original source files + for (auto s : source_files) { + std::ofstream dst(s, std::ios::binary); + std::string src_fn(s); + std::ifstream src(src_fn.append(".backup"), std::ios::binary); + + dst << src.rdbuf(); + + src.close(); + dst.close(); + } + } + else + err(1, "fork"); } diff --git a/tests/fibonacci/Makefile b/tests/fibonacci/Makefile @@ -1,5 +1,15 @@ +BIN = prog +SRCS = fib.c +OBJS = $(SRCS:c=o) + +$(BIN): $(OBJS) + $(CC) -o $(BIN) $(OBJS) $(LDLIBS) + test: @sh test.sh +diff: + @diff -u fib.c.inst fib.c.inst_good + clean: - @rm -f prog prog_inst.c + rm -f $(OBJS) $(BIN) *.backup diff --git a/tests/fibonacci/prog.c b/tests/fibonacci/fib.c diff --git a/tests/fibonacci/instrumented.c b/tests/fibonacci/fib_reference.c