commit 2db720d7babdf7233c369d817dec26ca303f8cff
parent c84e8963e659f905c961c9fb07b79f589f8f63e2
Author: Kyle Milz <kyle@getaddrinfo.net>
Date:   Thu, 24 Mar 2016 22:08:22 -0600
instrument: let it pass a configure run
- fix some bugs to allow instrument to be used during autoconf ./configure run
- mkdir() for the inst dir was wrong, fix that
- keep track of -c and -o flags, as this tells us when a link is attempted
  - links are important because it means we should reset our source counter
- also convert more stuff to c++ style idioms
Diffstat:
2 files changed, 107 insertions(+), 59 deletions(-)
diff --git a/instrument/instrumenter.cc b/instrument/instrumenter.cc
@@ -115,37 +115,46 @@ MyFrontendAction::CreateASTConsumer(CompilerInstance &CI, StringRef file)
 }
 
 unsigned int
-get_src_number()
+read_src_number()
 {
 	char *cwd = getcwd(NULL, PATH_MAX);
 	if (cwd == NULL)
 		errx(1, "getcwd");
+
 	std::string src_number_filename(cwd);
 	src_number_filename.append("/SRC_NUMBER");
 
-	std::fstream src_number_file;
 	if (access(src_number_filename.c_str(), F_OK) == -1) {
-		// SRC_NUMBER does not exist, create it
-		src_number_file.open(src_number_filename, std::fstream::out);
-		src_number_file << 0;
-		src_number_file.close();
-
-		// First source file is zero
+		// SRC_NUMBER does not exist, source number is 0
 		return 0;
 	}
 
-	// SRC_NUMBER existed, read its contents and write incremented value
-	src_number_file.open(src_number_filename, std::fstream::in | std::fstream::out);
-
+	// SRC_NUMBER exists, read its content
+	std::ifstream src_number_file;
 	unsigned int src_num = 0;
+
+	src_number_file.open(src_number_filename, std::fstream::in);
 	src_number_file >> src_num;
-	++src_num;
+	src_number_file.close();
 
-	// Write the new source number
-	src_number_file.seekg(0);
-	src_number_file << src_num;
+	// Pre-increment. The current source number is the last one plus one
+	return ++src_num;
+}
+
+void
+write_src_number(int src_num)
+{
+	char *cwd = getcwd(NULL, PATH_MAX);
+	if (cwd == NULL)
+		errx(1, "getcwd");
 
-	return src_num;
+	std::string src_number_filename(cwd);
+	src_number_filename.append("/SRC_NUMBER");
+
+	std::ofstream src_number_file;
+	src_number_file.open(src_number_filename, std::fstream::out);
+	src_number_file << src_num;
+	src_number_file.close();
 }
 
 void
@@ -163,7 +172,7 @@ MyFrontendAction::EndSourceFileAction()
 	unsigned int num_lines = sm.getPresumedLineNumber(end);
 
 	std::string file_name = getCurrentFile();
-	unsigned int tu_number = get_src_number();
+	unsigned int tu_number = read_src_number();
 
 	std::stringstream ss;
 	// Embed the header directly in the primary source file.
@@ -180,25 +189,34 @@ MyFrontendAction::EndSourceFileAction()
 	ss << "struct _scv_node _scv_node" << tu_number << " = {" << std::endl
 		<< "	.lines_ptr = _scv_lines," << std::endl
 		<< "	.size = " << num_lines << "," << std::endl
-		<< "	.file_name = \"" << file_name << "\"," << std::endl;
-		ss << "	.next = &_scv_node" << tu_number + 1 << "," << std::endl;
-	ss << "};" << std::endl;
+		<< "	.file_name = \"" << file_name << "\"," << std::endl
+		<< "	.next = &_scv_node" << tu_number + 1 << "," << std::endl
+		<< "};" << std::endl;
 
 	TheRewriter.InsertTextAfter(start, ss.str());
 
-	// write the instrumented source file to another directory
-	if (mkdir("inst", S_IWUSR | S_IRUSR | S_IXUSR))
-		// already existing directory is ok
+	size_t last_slash = file_name.find_last_of('/');
+	std::string base_dir(file_name.substr(0, last_slash + 1));
+	base_dir.append("inst");
+
+	if (mkdir(base_dir.c_str(), S_IWUSR | S_IRUSR | S_IXUSR))
 		if (errno != EEXIST)
+			// An error other than the directory existing occurred
 			err(1, "mkdir");
 
-	size_t last_slash = file_name.find_last_of('/');
 	file_name.insert(last_slash + 1, "inst/");
-	int fd = open(file_name.c_str(), O_WRONLY | O_CREAT,
-			S_IRUSR | S_IWUSR);
+
+	// Instrumented source file might already exist
+	unlink(file_name.c_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);
-	// TheRewriter.getEditBuffer(main_fid).write(llvm::outs());
+
+	// If we got this far write the new translation unit number
+	write_src_number(tu_number);
 }
diff --git a/instrument/main.cc b/instrument/main.cc
@@ -54,20 +54,23 @@ clean_path()
 	setenv("PATH", new_path.str().c_str(), 1);
 }
 
-void
-instrument(int argc, char *argv[], std::vector<std::string> &source_files)
+int
+instrument(int argc, char *argv[], std::vector<std::string> const &source_files)
 {
 	std::vector<const char *> clang_argv;
-	clang_argv.push_back(argv[0]);
 
+	if (source_files.size() == 0)
+		// Nothing to do
+		return 1;
+
+	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 after --
-	for (int i = 0; i < argc; i++)
-		clang_argv.push_back(argv[i]);
+	// Append original command line verbatim
+	clang_argv.insert(clang_argv.end(), argv, argv + argc);
 
 	// give clang it's <source files> -- <native command line> arg style
 	int clang_argc = clang_argv.size();
@@ -82,7 +85,8 @@ instrument(int argc, char *argv[], std::vector<std::string> &source_files)
 	// int ret = Tool.run(new MFAF(inst_files));
 	int ret = Tool.run(newFrontendActionFactory<MyFrontendAction>());
 	if (ret)
-		errx(1, "Instrumentation failed");
+		warnx("Instrumentation failed");
+	return ret;
 }
 
 bool
@@ -97,16 +101,26 @@ ends_with(std::string const &value, std::string const &suffix)
 int
 main(int argc, char *argv[])
 {
+	std::vector<std::string> args(argv, argv + argc);
+	std::vector<char *> modified_args;
 	std::vector<std::string> source_files;
-	std::vector<char *> real_compiler_argv;
-
-	for (int i = 0; i < argc; i++) {
-		std::string arg(argv[i]);
-
-		// copy argument verbatim for now, we'll replace later if needed
-		real_compiler_argv.push_back(argv[i]);
-
-		// Dirty hack to find source files
+	bool preprocess_arg = false;
+	bool object_arg = false;
+	bool compile_arg = false;
+
+	// Set a better name than the symlink that was used to find this program
+	setprogname("scv_instrument");
+
+	for (auto &arg : args) {
+		// Special case some hopefully universal arguments
+		if (arg.compare("-E") == 0)
+			preprocess_arg = true;
+		else if (arg.compare("-o") == 0)
+			object_arg = true;
+		else if (arg.compare("-c") == 0)
+			compile_arg = true;
+
+		// Find source files
 		if (ends_with(arg, ".cpp") || ends_with(arg, ".c")
 		    || ends_with(arg, ".cxx")) {
 
@@ -129,33 +143,49 @@ main(int argc, char *argv[])
 			if (src_name == NULL)
 				err(1, "basename");
 
-			std::string inst_src_path;
-			inst_src_path.append(src_dir);
-			inst_src_path.append("/inst/");
-			inst_src_path.append(src_name);
+			// modified_args will hang onto the contents of this
+			std::string *inst_src_path = new std::string();
+			inst_src_path->append(src_dir);
+			inst_src_path->append("/inst/");
+			inst_src_path->append(src_name);
 
-			// Compilation file will be instrumented source
-			real_compiler_argv.at(i) = strdup(inst_src_path.c_str());
+			// Switch the original file name with the instrumented
+			// one.
+			modified_args.push_back(&(*inst_src_path)[0]);
+			continue;
 		}
+
+		// Non source file argument, copy verbatim
+		modified_args.push_back((char *)arg.c_str());
 	}
 
-	// NULL terminate arguments we will be passing to exec*()
-	real_compiler_argv.push_back(NULL);
+	// NULL terminate the arg vectors we pass to exec()
+	modified_args.push_back(NULL);
 	argv[argc] = NULL;
 
-	if (source_files.size() == 0) {
-		// We didn't detect any source code on the command line
+	// -o with -c means output object file
+	// -o without -c means output binary
+	if (object_arg && !compile_arg) {
+		char *cwd = getcwd(NULL, PATH_MAX);
+		if (cwd == NULL)
+			errx(1, "getcwd");
+
+		std::string src_number_filename(cwd);
+		src_number_filename.append("/SRC_NUMBER");
+		unlink(src_number_filename.c_str());
+	}
+
+	if (preprocess_arg || instrument(argc, argv, source_files)) {
+		// The preprocessor arg was found or instrumentation failed.
+		// In Either case, run the native command unmodified.
 		clean_path();
 		if (execvp(argv[0], argv))
 			err(1, "execvp");
 	}
 
-	// Instument the detected source files, storing them in inst/
-	instrument(argc, argv, source_files);
-
-	// Run the native compiler on the *instrumented* source files.
-	// Source file name substitution has already been done.
+	// Instrumentation succeeded. Run the native compiler with a modified
+	// command line.
 	clean_path();
-	if (execvp(real_compiler_argv[0], &real_compiler_argv[0]))
+	if (execvp(modified_args[0], &modified_args[0]))
 		err(1, "execvp");
 }