commit e1779a75450ac26b935c66e49f5a85108568c2e2
parent 8172e1b82d397f945be4450530ab853cc46eb533
Author: Kyle Milz <kyle@0x30.net>
Date: Sat, 13 Aug 2016 04:04:42 -0600
src: split inst_main into two
Diffstat:
4 files changed, 298 insertions(+), 275 deletions(-)
diff --git a/src/Jamfile b/src/Jamfile
@@ -55,6 +55,7 @@ Main citrun-gl : $(GL_SRCS) ;
#
INST_SRCS =
inst_main.cc
+ inst_frontend.cc
inst_action.cc
inst_visitor.cc ;
diff --git a/src/inst_frontend.cc b/src/inst_frontend.cc
@@ -0,0 +1,294 @@
+//
+// Copyright (c) 2016 Kyle Milz <kyle@0x30.net>
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+#include "inst_frontend.h"
+
+#include <sys/stat.h> // stat
+#include <sys/time.h> // utimes
+#include <sys/wait.h> // waitpid
+
+#include <clang/Frontend/TextDiagnosticPrinter.h>
+#include <clang/Tooling/CommonOptionsParser.h>
+#include <clang/Tooling/Tooling.h>
+#include <cstdio> // tmpnam
+#include <cstring> // strcmp
+#include <err.h>
+#include <fstream> // ifstream, ofstream
+#include <libgen.h> // basename
+#include <unistd.h> // execvp, fork, getpid, unlink
+
+#define STR_EXPAND(tok) #tok
+#define STR(tok) STR_EXPAND(tok)
+
+static llvm::cl::OptionCategory ToolingCategory("citrun-inst options");
+
+CitrunInst::CitrunInst(int argc, char *argv[], InstrumentLogger &l, bool is_citruninst) :
+ m_args(argv, argv + argc),
+ m_log(l),
+ m_is_citruninst(is_citruninst)
+{
+}
+
+// Returns true if value ends with suffix, false otherwise.
+static 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());
+}
+
+// Copies one file to another preserving timestamps.
+static void
+copy_file(std::string const &dst_fn, std::string const &src_fn)
+{
+ struct stat sb;
+ struct timeval st_tim[2];
+
+ // Save original access and modification times
+ stat(src_fn.c_str(), &sb);
+#ifdef __APPLE__
+ TIMESPEC_TO_TIMEVAL(&st_tim[0], &sb.st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&st_tim[1], &sb.st_mtimespec);
+#else
+ TIMESPEC_TO_TIMEVAL(&st_tim[0], &sb.st_atim);
+ TIMESPEC_TO_TIMEVAL(&st_tim[1], &sb.st_mtim);
+#endif
+
+ std::ifstream src(src_fn, std::ios::binary);
+ std::ofstream dst(dst_fn, std::ios::binary);
+
+ dst << src.rdbuf();
+
+ src.close();
+ dst.close();
+
+ // Restore the original access and modification time
+ utimes(dst_fn.c_str(), st_tim);
+}
+
+void
+CitrunInst::save_if_srcfile(char *arg)
+{
+ if (ends_with(arg, ".c") || ends_with(arg, ".cc") ||
+ ends_with(arg, ".cpp") || ends_with(arg, ".cxx")) {
+
+ m_source_files.push_back(arg);
+ m_log << "Found source file '" << arg << "'.\n";
+
+ if (m_is_citruninst)
+ // In this mode the modified source file is written to a
+ // completely different file.
+ return;
+
+ char *dst_fn;
+ if ((dst_fn = std::tmpnam(NULL)) == NULL)
+ err(1, "tmpnam");
+
+ copy_file(dst_fn, arg);
+ m_temp_file_map[arg] = dst_fn;
+ }
+}
+
+bool
+CitrunInst::is_link_cmd(bool object_arg, bool compile_arg)
+{
+ if (!object_arg && !compile_arg && m_source_files.size() > 0)
+ // Assume single line a.out compilation
+ // $ gcc main.c
+ return true;
+ else if (object_arg && !compile_arg)
+ // gcc -o main main.o fib.o while.o
+ // gcc -o main main.c fib.c
+ return true;
+
+ return false;
+}
+
+void
+CitrunInst::process_cmdline()
+{
+ bool object_arg = false;
+ bool compile_arg = false;
+
+ m_log << "Command line is '";
+ for (auto &arg : m_args)
+ m_log << arg << " ";
+ m_log << "'.\n";
+
+ for (auto &arg : m_args) {
+
+ if (std::strcmp(arg, "-E") == 0) {
+ m_log << "Preprocessor argument found\n";
+ exec_compiler();
+ }
+ else if (std::strcmp(arg, "-o") == 0)
+ object_arg = true;
+ else if (std::strcmp(arg, "-c") == 0)
+ compile_arg = true;
+
+ save_if_srcfile(arg);
+ }
+
+ m_log << "Object arg = " << object_arg << ", "
+ << "compile arg = " << compile_arg << "\n";
+
+ if (is_link_cmd(object_arg, compile_arg)) {
+ m_log << "Link detected, adding '";
+#ifndef __APPLE__
+ // OSX always links this.
+ m_args.push_back(const_cast<char *>("-pthread"));
+ m_log << m_args.back() << " ";
+#endif
+#ifdef CITRUN_COVERAGE
+ // Needed because libcitrun.a will be instrumented with gcov.
+ m_args.push_back(const_cast<char *>("-coverage"));
+#endif
+ m_args.push_back(const_cast<char *>(STR(CITRUN_SHARE) "/libcitrun.a"));
+ m_log << m_args.back() << "' to command line.\n";
+ }
+
+ if (m_source_files.size() != 0)
+ return;
+
+ m_log << "No source files found. Executing command line.\n";
+ exec_compiler();
+}
+
+int
+CitrunInst::instrument()
+{
+ //
+ // Create a special command line for ClangTool that looks like:
+ // clang++ src1.c src2.c -- clang++ -I. -Isrc -c src1.c src2.c
+ //
+ std::vector<const char *> clang_argv;
+ clang_argv.push_back(m_args[0]);
+ for (auto s : m_source_files)
+ clang_argv.push_back(s.c_str());
+ clang_argv.push_back("--");
+ clang_argv.insert(clang_argv.end(), m_args.begin(), m_args.end());
+#if defined(__OpenBSD__)
+ clang_argv.push_back("-I/usr/local/lib/clang/3.8.1/include");
+ m_log << "Added clangtool argument '" << clang_argv.back() << "'.\n";
+#elif defined(__APPLE__)
+ clang_argv.push_back("-I/opt/local/libexec/llvm-3.8/lib/clang/3.8.1/include");
+ m_log << "Added clangtool argument '" << clang_argv.back() << "'.\n";
+#endif
+
+ int clang_argc = clang_argv.size();
+ clang::tooling::CommonOptionsParser
+ op(clang_argc, &clang_argv[0], ToolingCategory);
+ clang::tooling::ClangTool
+ Tool(op.getCompilations(), op.getSourcePathList());
+
+ clang::DiagnosticOptions diags;
+ clang::TextDiagnosticPrinter *log;
+
+ log = new clang::TextDiagnosticPrinter(*m_log.m_output, &diags, false);
+ log->setPrefix(std::to_string(m_log.m_pid));
+ Tool.setDiagnosticConsumer(log);
+
+ std::unique_ptr<InstrumentActionFactory> f =
+ llvm::make_unique<InstrumentActionFactory>(m_log, m_is_citruninst, m_source_files);
+
+ int ret = Tool.run(f.get());
+ m_log << "Instrumentation " << (ret ? "failed.\n" : "successful.\n");
+ return ret;
+}
+
+int
+CitrunInst::try_unmodified_compile()
+{
+ restore_original_src();
+ int ret = fork_compiler();
+
+ if (ret == 0) {
+ m_log << "But the native compile succeeded!\n";
+ return 1;
+ }
+
+ m_log << "And the native compile failed.\n";
+ return ret;
+}
+
+void
+CitrunInst::restore_original_src()
+{
+ for (auto &tmp_file : m_temp_file_map) {
+ m_log << "Restored '" << tmp_file.first << "'.\n";
+
+ copy_file(tmp_file.first, tmp_file.second);
+ unlink(tmp_file.second.c_str());
+ }
+}
+
+void
+CitrunInst::exec_compiler()
+{
+ // XXX: Need to destroy log here.
+ m_log.m_output->flush();
+
+ if (m_is_citruninst) {
+ m_log << "Running as citrun-inst, not re-exec()'ing\n";
+ exit(0);
+ }
+
+ m_args.push_back(NULL);
+ if (execvp(m_args[0], &m_args[0]))
+ err(1, "execvp");
+}
+
+int
+CitrunInst::fork_compiler()
+{
+ // Otherwise we'll get two copies of buffers after fork().
+ m_log.m_output->flush();
+
+ pid_t child_pid;
+ if ((child_pid = fork()) < 0)
+ err(1, "fork");
+
+ if (child_pid == 0)
+ // In child.
+ exec_compiler();
+
+ m_log << "Forked '" << m_args[0] << "' "
+ << "pid is '" << child_pid << "'.\n";
+
+ int status;
+ if (waitpid(child_pid, &status, 0) < 0)
+ err(1, "waitpid");
+
+ // Return the exit code of the native compiler.
+ int exit = -1;
+ if (WIFEXITED(status))
+ exit = WEXITSTATUS(status);
+
+ m_log << "'" << child_pid << "' exited " << exit << ".\n";
+ return exit;
+}
+
+int
+CitrunInst::compile_modified()
+{
+ m_log << "Running native compiler on modified source code.\n";
+
+ int ret = fork_compiler();
+ restore_original_src();
+
+ return ret;
+}
diff --git a/src/inst_main.h b/src/inst_frontend.h
diff --git a/src/inst_main.cc b/src/inst_main.cc
@@ -13,32 +13,19 @@
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
-#include <sys/stat.h> // stat
-#include <sys/time.h> // utimes
+#include "runtime.h" // citrun_major, citrun_minor
+#include "inst_frontend.h"
+
#include <sys/utsname.h> // uname
-#include <sys/wait.h> // waitpid
-#include <clang/Frontend/TextDiagnosticPrinter.h>
-#include <clang/Tooling/CommonOptionsParser.h>
-#include <clang/Tooling/Tooling.h>
-#include <cstdio> // tmpnam
-#include <cstdlib> // getenv
#include <cstring> // strcmp
#include <err.h>
-#include <fstream> // ifstream, ofstream
#include <libgen.h> // basename
-#include <iostream>
#include <sstream> // stringstream
-#include <unistd.h> // execvp, fork, getpid, unlink
-
-#include "runtime.h" // citrun_major, citrun_minor
-#include "inst_main.h"
#define STR_EXPAND(tok) #tok
#define STR(tok) STR_EXPAND(tok)
-static llvm::cl::OptionCategory ToolingCategory("citrun-inst options");
-
int
clean_PATH(InstrumentLogger &llog)
{
@@ -139,262 +126,3 @@ main(int argc, char *argv[])
return main.compile_modified();
}
-
-CitrunInst::CitrunInst(int argc, char *argv[], InstrumentLogger &l, bool is_citruninst) :
- m_args(argv, argv + argc),
- m_log(l),
- m_is_citruninst(is_citruninst)
-{
-}
-
-// Returns true if value ends with suffix, false otherwise.
-static 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());
-}
-
-// Copies one file to another preserving timestamps.
-static void
-copy_file(std::string const &dst_fn, std::string const &src_fn)
-{
- struct stat sb;
- struct timeval st_tim[2];
-
- // Save original access and modification times
- stat(src_fn.c_str(), &sb);
-#ifdef __APPLE__
- TIMESPEC_TO_TIMEVAL(&st_tim[0], &sb.st_atimespec);
- TIMESPEC_TO_TIMEVAL(&st_tim[1], &sb.st_mtimespec);
-#else
- TIMESPEC_TO_TIMEVAL(&st_tim[0], &sb.st_atim);
- TIMESPEC_TO_TIMEVAL(&st_tim[1], &sb.st_mtim);
-#endif
-
- std::ifstream src(src_fn, std::ios::binary);
- std::ofstream dst(dst_fn, std::ios::binary);
-
- dst << src.rdbuf();
-
- src.close();
- dst.close();
-
- // Restore the original access and modification time
- utimes(dst_fn.c_str(), st_tim);
-}
-
-void
-CitrunInst::save_if_srcfile(char *arg)
-{
- if (ends_with(arg, ".c") || ends_with(arg, ".cc") ||
- ends_with(arg, ".cpp") || ends_with(arg, ".cxx")) {
-
- m_source_files.push_back(arg);
- m_log << "Found source file '" << arg << "'.\n";
-
- if (m_is_citruninst)
- // In this mode the modified source file is written to a
- // completely different file.
- return;
-
- char *dst_fn;
- if ((dst_fn = std::tmpnam(NULL)) == NULL)
- err(1, "tmpnam");
-
- copy_file(dst_fn, arg);
- m_temp_file_map[arg] = dst_fn;
- }
-}
-
-bool
-CitrunInst::is_link_cmd(bool object_arg, bool compile_arg)
-{
- if (!object_arg && !compile_arg && m_source_files.size() > 0)
- // Assume single line a.out compilation
- // $ gcc main.c
- return true;
- else if (object_arg && !compile_arg)
- // gcc -o main main.o fib.o while.o
- // gcc -o main main.c fib.c
- return true;
-
- return false;
-}
-
-void
-CitrunInst::process_cmdline()
-{
- bool object_arg = false;
- bool compile_arg = false;
-
- m_log << "Command line is '";
- for (auto &arg : m_args)
- m_log << arg << " ";
- m_log << "'.\n";
-
- for (auto &arg : m_args) {
-
- if (std::strcmp(arg, "-E") == 0) {
- m_log << "Preprocessor argument found\n";
- exec_compiler();
- }
- else if (std::strcmp(arg, "-o") == 0)
- object_arg = true;
- else if (std::strcmp(arg, "-c") == 0)
- compile_arg = true;
-
- save_if_srcfile(arg);
- }
-
- m_log << "Object arg = " << object_arg << ", "
- << "compile arg = " << compile_arg << "\n";
-
- if (is_link_cmd(object_arg, compile_arg)) {
- m_log << "Link detected, adding '";
-#ifndef __APPLE__
- // OSX always links this.
- m_args.push_back(const_cast<char *>("-pthread"));
- m_log << m_args.back() << " ";
-#endif
-#ifdef CITRUN_COVERAGE
- // Needed because libcitrun.a will be instrumented with gcov.
- m_args.push_back(const_cast<char *>("-coverage"));
-#endif
- m_args.push_back(const_cast<char *>(STR(CITRUN_SHARE) "/libcitrun.a"));
- m_log << m_args.back() << "' to command line.\n";
- }
-
- if (m_source_files.size() != 0)
- return;
-
- m_log << "No source files found. Executing command line.\n";
- exec_compiler();
-}
-
-int
-CitrunInst::instrument()
-{
- //
- // Create a special command line for ClangTool that looks like:
- // clang++ src1.c src2.c -- clang++ -I. -Isrc -c src1.c src2.c
- //
- std::vector<const char *> clang_argv;
- clang_argv.push_back(m_args[0]);
- for (auto s : m_source_files)
- clang_argv.push_back(s.c_str());
- clang_argv.push_back("--");
- clang_argv.insert(clang_argv.end(), m_args.begin(), m_args.end());
-#if defined(__OpenBSD__)
- clang_argv.push_back("-I/usr/local/lib/clang/3.8.1/include");
- m_log << "Added clangtool argument '" << clang_argv.back() << "'.\n";
-#elif defined(__APPLE__)
- clang_argv.push_back("-I/opt/local/libexec/llvm-3.8/lib/clang/3.8.1/include");
- m_log << "Added clangtool argument '" << clang_argv.back() << "'.\n";
-#endif
-
- int clang_argc = clang_argv.size();
- clang::tooling::CommonOptionsParser
- op(clang_argc, &clang_argv[0], ToolingCategory);
- clang::tooling::ClangTool
- Tool(op.getCompilations(), op.getSourcePathList());
-
- clang::DiagnosticOptions diags;
- clang::TextDiagnosticPrinter *log;
-
- log = new clang::TextDiagnosticPrinter(*m_log.m_output, &diags, false);
- log->setPrefix(std::to_string(m_log.m_pid));
- Tool.setDiagnosticConsumer(log);
-
- std::unique_ptr<InstrumentActionFactory> f =
- llvm::make_unique<InstrumentActionFactory>(m_log, m_is_citruninst, m_source_files);
-
- int ret = Tool.run(f.get());
- m_log << "Instrumentation " << (ret ? "failed.\n" : "successful.\n");
- return ret;
-}
-
-int
-CitrunInst::try_unmodified_compile()
-{
- restore_original_src();
- int ret = fork_compiler();
-
- if (ret == 0) {
- m_log << "But the native compile succeeded!\n";
- return 1;
- }
-
- m_log << "And the native compile failed.\n";
- return ret;
-}
-
-void
-CitrunInst::restore_original_src()
-{
- for (auto &tmp_file : m_temp_file_map) {
- m_log << "Restored '" << tmp_file.first << "'.\n";
-
- copy_file(tmp_file.first, tmp_file.second);
- unlink(tmp_file.second.c_str());
- }
-}
-
-void
-CitrunInst::exec_compiler()
-{
- // XXX: Need to destroy log here.
- m_log.m_output->flush();
-
- if (m_is_citruninst) {
- m_log << "Running as citrun-inst, not re-exec()'ing\n";
- exit(0);
- }
-
- m_args.push_back(NULL);
- if (execvp(m_args[0], &m_args[0]))
- err(1, "execvp");
-}
-
-int
-CitrunInst::fork_compiler()
-{
- // Otherwise we'll get two copies of buffers after fork().
- m_log.m_output->flush();
-
- pid_t child_pid;
- if ((child_pid = fork()) < 0)
- err(1, "fork");
-
- if (child_pid == 0)
- // In child.
- exec_compiler();
-
- m_log << "Forked '" << m_args[0] << "' "
- << "pid is '" << child_pid << "'.\n";
-
- int status;
- if (waitpid(child_pid, &status, 0) < 0)
- err(1, "waitpid");
-
- // Return the exit code of the native compiler.
- int exit = -1;
- if (WIFEXITED(status))
- exit = WEXITSTATUS(status);
-
- m_log << "'" << child_pid << "' exited " << exit << ".\n";
- return exit;
-}
-
-int
-CitrunInst::compile_modified()
-{
- m_log << "Running native compiler on modified source code.\n";
-
- int ret = fork_compiler();
- restore_original_src();
-
- return ret;
-}