commit 4044fe3f56d911a9d476cce474264c057cc17bef
parent 384359e73cc57b35bca5f6eebf66904304f3dec9
Author: Kyle Milz <kyle@0x30.net>
Date: Sat, 30 Jul 2016 22:49:52 -0600
src: add CitrunInst class
Diffstat:
M | src/inst_main.cc | | | 304 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
1 file changed, 163 insertions(+), 141 deletions(-)
diff --git a/src/inst_main.cc b/src/inst_main.cc
@@ -36,57 +36,115 @@
static llvm::cl::OptionCategory ToolingCategory("instrument options");
-void
-clean_path()
+class CitrunInst {
+public:
+ CitrunInst(int, char *argv[]);
+ int instrument();
+ void patch_link_command();
+ int fork_compiler();
+ void restore_original_src();
+
+private:
+ std::vector<char *> m_args;
+ std::vector<std::string> m_source_files;
+ std::map<std::string, std::string> m_temp_file_map;
+ bool m_object_arg;
+ bool m_compile_arg;
+};
+
+// Returns true if value ends with suffix, false otherwise.
+static bool
+ends_with(std::string const &value, std::string const &suffix)
{
- char *path;
+ if (suffix.length() > value.length())
+ return false;
- if ((path = std::getenv("PATH")) == NULL)
- errx(1, "PATH must be set");
+ return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin());
+}
- // Filter CITRUN_PATH out of PATH
- std::stringstream path_ss(path);
- std::ostringstream new_path;
- std::string component;
- bool first_component = 1;
- bool found_citrun_path = 0;
+// 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];
- while (std::getline(path_ss, component, ':')) {
- if (component.compare(STR(CITRUN_PATH)) == 0) {
- found_citrun_path = 1;
- continue;
+ // 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);
+}
+
+CitrunInst::CitrunInst(int argc, char *argv[]) :
+ m_args(argv, argv + argc),
+ m_object_arg(false),
+ m_compile_arg(false)
+{
+ for (auto &arg : m_args) {
+ if (std::strcmp(arg, "-E") == 0) {
+ // Preprocessing argument found, exec native command
+ m_args.push_back(NULL);
+ if (execvp(m_args[0], &m_args[0]))
+ err(1, "execvp");
}
+ else if (std::strcmp(arg, "-o") == 0)
+ m_object_arg = true;
+ else if (std::strcmp(arg, "-c") == 0)
+ m_compile_arg = true;
- if (first_component == 0)
- new_path << ":";
+ // Find source files
+ if (ends_with(arg, ".c") || ends_with(arg, ".cc") ||
+ ends_with(arg, ".cpp") || ends_with(arg, ".cxx")) {
- // It wasn't CITRUN_PATH, keep it
- new_path << component;
- first_component = 0;
- }
+ // Keep track of original source file names
+ m_source_files.push_back(arg);
- if (!found_citrun_path)
- errx(1, "'%s' not in PATH", STR(CITRUN_PATH));
+ if (std::getenv("CITRUN_TESTING"))
+ // Don't copy and restore original source files
+ continue;
- // Set new $PATH
- setenv("PATH", new_path.str().c_str(), 1);
+ 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;
+ }
+ }
}
int
-instrument(int argc, char *argv[], std::vector<std::string> const &source_files)
+CitrunInst::instrument()
{
- if (source_files.size() == 0)
+ std::vector<const char *> clang_argv;
+
+ if (m_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(m_args[0]);
+ for (auto s : m_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);
+ clang_argv.insert(clang_argv.end(), m_args.begin(), m_args.end());
// We should be able to get this programmatically, but I don't know how.
#if defined(__OpenBSD__)
@@ -107,71 +165,64 @@ instrument(int argc, char *argv[], std::vector<std::string> const &source_files)
// further customize this, we could create our own factory class.
// int ret = Tool.run(new MFAF(inst_files));
int ret = Tool.run(&(*clang::tooling::newFrontendActionFactory<InstrumentAction>()));
- 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 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);
+ if (ret == 0)
+ return 0;
- dst << src.rdbuf();
+ // Instrumentation failed, try compiling with native compiler.
+ restore_original_src();
+ ret = fork_compiler();
- src.close();
- dst.close();
+ // Native compiler succeeded. This is bad.
+ if (ret == 0) {
+ warnx("citrun instrumentation failed, but the native "
+ "compile succeeded!");
+ return 0;
+ }
- // Restore the original access and modification time
- utimes(dst_fn.c_str(), st_tim);
+ // Native compiler failed too. That's okay.
+ return ret;
}
void
-restore_original_src(std::map<std::string, std::string> const &temp_file_map)
+CitrunInst::restore_original_src()
{
- for (auto &tmp_file : temp_file_map) {
+ for (auto &tmp_file : m_temp_file_map) {
copy_file(tmp_file.first, tmp_file.second);
unlink(tmp_file.second.c_str());
}
}
void
-patch_link_command(std::vector<char *> &args)
+CitrunInst::patch_link_command()
{
+ bool linking = false;
+
+ if (!m_object_arg && !m_compile_arg && m_source_files.size() > 0)
+ // Assume single line a.out compilation
+ // $ gcc main.c
+ linking = true;
+ else if (m_object_arg && !m_compile_arg)
+ // gcc -o main main.o fib.o while.o
+ // gcc -o main main.c fib.c
+ linking = true;
+
+ if (!linking)
+ return;
+
// libcitrun.a uses pthread so we must link it here.
#ifndef __APPLE__
// Except Mac OS, who always links this.
- args.push_back(const_cast<char *>("-pthread"));
+ m_args.push_back(const_cast<char *>("-pthread"));
#endif
- args.push_back(const_cast<char *>(STR(CITRUN_LIB)));
+ m_args.push_back(const_cast<char *>(STR(CITRUN_LIB)));
}
int
-fork_compiler(std::vector<char *> &args)
+CitrunInst::fork_compiler()
{
- // args must be NULL terminated for exec*() functions.
- args.push_back(NULL);
+ // m_args must be NULL terminated for exec*() functions.
+ m_args.push_back(NULL);
pid_t child_pid;
if ((child_pid = fork()) < 0)
@@ -179,7 +230,7 @@ fork_compiler(std::vector<char *> &args)
if (child_pid == 0) {
// In child
- if (execvp(args[0], &args[0]))
+ if (execvp(m_args[0], &m_args[0]))
err(1, "execvp");
}
@@ -194,87 +245,58 @@ fork_compiler(std::vector<char *> &args)
return -1;
}
-int
-main(int argc, char *argv[])
+void
+clean_path()
{
- // We probably didn't call citrun-inst directly.
- setprogname("citrun-inst");
- clean_path();
+ char *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;
+ if ((path = std::getenv("PATH")) == NULL)
+ errx(1, "PATH must be set");
- // Necessary because we're going to pass argv to execvp
- argv[argc] = NULL;
+ // Filter CITRUN_PATH out of PATH
+ std::stringstream path_ss(path);
+ std::ostringstream new_path;
+ std::string component;
+ bool first_component = 1;
+ bool found_citrun_path = 0;
- for (auto &arg : args) {
- if (std::strcmp(arg, "-E") == 0) {
- // Preprocessing argument found, exec native command
- if (execvp(argv[0], argv))
- err(1, "execvp");
+ while (std::getline(path_ss, component, ':')) {
+ if (component.compare(STR(CITRUN_PATH)) == 0) {
+ found_citrun_path = 1;
+ continue;
}
- else if (std::strcmp(arg, "-o") == 0)
- object_arg = true;
- else if (std::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 (std::getenv("CITRUN_TESTING"))
- // Don't copy and restore original source files
- continue;
- char *dst_fn;
- if ((dst_fn = std::tmpnam(NULL)) == NULL)
- err(1, "tmpnam");
+ if (first_component == 0)
+ new_path << ":";
- copy_file(dst_fn, arg);
- temp_file_map[arg] = dst_fn;
- }
+ // It wasn't CITRUN_PATH, keep it
+ new_path << component;
+ first_component = 0;
}
- if (instrument(argc, argv, source_files)) {
- // Instrumentation failed.
- restore_original_src(temp_file_map);
-
- // Try compile with native compiler.
- int ret = fork_compiler(args);
+ if (!found_citrun_path)
+ errx(1, "'%s' not in PATH", STR(CITRUN_PATH));
- // This is bad.
- if (ret == 0) {
- warnx("citrun instrumentation failed, but the native"
- "compile succeeded!");
- return 0;
- }
+ // Set new $PATH
+ if (setenv("PATH", new_path.str().c_str(), 1))
+ err(1, "setenv");
+}
- // Native compiler failed too. That's okay.
- return ret;
- }
+int
+main(int argc, char *argv[])
+{
+ // We probably didn't call citrun-inst directly.
+ setprogname("citrun-inst");
+ clean_path();
- 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;
+ CitrunInst main(argc, argv);
- if (linking)
- patch_link_command(args);
+ if (main.instrument())
+ return 1;
- int ret = fork_compiler(args);
- restore_original_src(temp_file_map);
+ main.patch_link_command();
+ int ret = main.fork_compiler();
+ main.restore_original_src();
return ret;
}