New files for miscompilation detection


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@5120 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/bugpoint/SystemUtils.cpp b/tools/bugpoint/SystemUtils.cpp
new file mode 100644
index 0000000..5483f80
--- /dev/null
+++ b/tools/bugpoint/SystemUtils.cpp
@@ -0,0 +1,189 @@
+//===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===//
+//
+// This file contains functions used to do a variety of low-level, often
+// system-specific, tasks.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SystemUtils.h"
+#include <algorithm>
+#include <fstream>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+/// removeFile - Delete the specified file
+///
+void removeFile(const std::string &Filename) {
+  unlink(Filename.c_str());
+}
+
+/// getUniqueFilename - Return a filename with the specified prefix.  If the
+/// file does not exist yet, return it, otherwise add a suffix to make it
+/// unique.
+///
+std::string getUniqueFilename(const std::string &FilenameBase) {
+  if (!std::ifstream(FilenameBase.c_str()))
+    return FilenameBase;    // Couldn't open the file? Use it!
+
+  // Create a pattern for mkstemp...
+  char *FNBuffer = (char*)alloca(FilenameBase.size()+8);
+  strcpy(FNBuffer, FilenameBase.c_str());
+  strcpy(FNBuffer+FilenameBase.size(), "-XXXXXX");
+
+  // Agree on a temporary file name to use....
+  int TempFD;
+  if ((TempFD = mkstemp(FNBuffer)) == -1) {
+    std::cerr << "bugpoint: ERROR: Cannot create temporary file in the current "
+	      << " directory!\n";
+    exit(1);
+  }
+
+  // We don't need to hold the temp file descriptor... we will trust that noone
+  // will overwrite/delete the file while we are working on it...
+  close(TempFD);
+  return FNBuffer;
+}
+
+/// isExecutableFile - This function returns true if the filename specified
+/// exists and is executable.
+///
+bool isExecutableFile(const std::string &ExeFileName) {
+  struct stat Buf;
+  if (stat(ExeFileName.c_str(), &Buf))
+    return false;  // Must not be executable!
+
+  if (!(Buf.st_mode & S_IFREG))
+    return false;                    // Not a regular file?
+
+  if (Buf.st_uid == getuid())        // Owner of file?
+    return Buf.st_mode & S_IXUSR;
+  else if (Buf.st_gid == getgid())   // In group of file?
+    return Buf.st_mode & S_IXGRP;
+  else                               // Unrelated to file?
+    return Buf.st_mode & S_IXOTH;
+}
+
+
+// FindExecutable - Find a named executable, giving the argv[0] of bugpoint.
+// This assumes the executable is in the same directory as bugpoint itself.
+// If the executable cannot be found, return an empty string.
+//
+std::string FindExecutable(const std::string &ExeName,
+			   const std::string &BugPointPath) {
+  // First check the directory that bugpoint is in.  We can do this if
+  // BugPointPath contains at least one / character, indicating that it is a
+  // relative path to bugpoint itself.
+  //
+  std::string Result = BugPointPath;
+  while (!Result.empty() && Result[Result.size()-1] != '/')
+    Result.erase(Result.size()-1, 1);
+
+  if (!Result.empty()) {
+    Result += ExeName;
+    if (isExecutableFile(Result)) return Result; // Found it?
+  }
+
+  // Okay, if the path to bugpoint didn't tell us anything, try using the PATH
+  // environment variable.
+  const char *PathStr = getenv("PATH");
+  if (PathStr == 0) return "";
+
+  // Now we have a colon seperated list of directories to search... try them...
+  unsigned PathLen = strlen(PathStr);
+  while (PathLen) {
+    // Find the first colon...
+    const char *Colon = std::find(PathStr, PathStr+PathLen, ':');
+    
+    // Check to see if this first directory contains the executable...
+    std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName;
+    if (isExecutableFile(FilePath))
+      return FilePath;                    // Found the executable!
+   
+    // Nope it wasn't in this directory, check the next range!
+    PathLen -= Colon-PathStr;
+    PathStr = Colon;
+    while (*PathStr == ':') {   // Advance past colons
+      PathStr++;
+      PathLen--;
+    }
+  }
+
+  // If we fell out, we ran out of directories in PATH to search, return failure
+  return "";
+}
+
+static void RedirectFD(const std::string &File, int FD) {
+  if (File.empty()) return;  // Noop
+
+  // Open the file
+  int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
+  if (InFD == -1) {
+    std::cerr << "Error opening file '" << File << "' for "
+	      << (FD == 0 ? "input" : "output") << "!\n";
+    exit(1);
+  }
+
+  dup2(InFD, FD);   // Install it as the requested FD
+  close(InFD);      // Close the original FD
+}
+
+/// RunProgramWithTimeout - This function executes the specified program, with
+/// the specified null-terminated argument array, with the stdin/out/err fd's
+/// redirected, with a timeout specified on the commandline.  This terminates
+/// the calling program if there is an error executing the specified program.
+/// It returns the return value of the program, or -1 if a timeout is detected.
+///
+int RunProgramWithTimeout(const std::string &ProgramPath, const char **Args,
+			  const std::string &StdInFile,
+			  const std::string &StdOutFile,
+			  const std::string &StdErrFile) {
+
+  // FIXME: install sigalarm handler here for timeout...
+
+  int Child = fork();
+  switch (Child) {
+  case -1:
+    std::cerr << "ERROR forking!\n";
+    exit(1);
+  case 0:               // Child
+    RedirectFD(StdInFile, 0);      // Redirect file descriptors...
+    RedirectFD(StdOutFile, 1);
+    RedirectFD(StdErrFile, 2);
+
+    execv(ProgramPath.c_str(), (char *const *)Args);
+    std::cerr << "Error executing program '" << ProgramPath;
+    for (; *Args; ++Args)
+      std::cerr << " " << *Args;
+    exit(1);
+
+  default: break;
+  }
+
+  // Make sure all output has been written while waiting
+  std::cout << std::flush;
+
+  int Status;
+  if (wait(&Status) != Child) {
+    if (errno == EINTR) {
+      static bool FirstTimeout = true;
+      if (FirstTimeout) {
+	std::cout <<
+ "*** Program execution timed out!  This mechanism is designed to handle\n"
+ "    programs stuck in infinite loops gracefully.  The -timeout option\n"
+ "    can be used to change the timeout threshold or disable it completely\n"
+ "    (with -timeout=0).  This message is only displayed once.\n";
+	FirstTimeout = false;
+      }
+      return -1;   // Timeout detected
+    }
+
+    std::cerr << "Error waiting for child process!\n";
+    exit(1);
+  }
+  return Status;
+}