[llvm-commits] [parallel] CVS: llvm/lib/Support/Annotation.cpp LeakDetector.cpp Mangler.cpp PluginLoader.cpp Signals.cpp ToolRunner.cpp
Misha Brukman
brukman at cs.uiuc.edu
Mon Mar 1 18:03:30 PST 2004
Changes in directory llvm/lib/Support:
Annotation.cpp updated: 1.13 -> 1.13.2.1
LeakDetector.cpp updated: 1.6 -> 1.6.2.1
Mangler.cpp updated: 1.8 -> 1.8.2.1
PluginLoader.cpp updated: 1.8 -> 1.8.2.1
Signals.cpp updated: 1.11 -> 1.11.2.1
ToolRunner.cpp updated: 1.13 -> 1.13.2.1
---
Log message:
Merge from trunk
---
Diffs of the changes: (+267 -153)
Index: llvm/lib/Support/Annotation.cpp
diff -u llvm/lib/Support/Annotation.cpp:1.13 llvm/lib/Support/Annotation.cpp:1.13.2.1
--- llvm/lib/Support/Annotation.cpp:1.13 Sun Dec 14 15:35:53 2003
+++ llvm/lib/Support/Annotation.cpp Mon Mar 1 17:58:13 2004
@@ -15,6 +15,18 @@
#include "Support/Annotation.h"
using namespace llvm;
+Annotation::~Annotation() {} // Designed to be subclassed
+
+Annotable::~Annotable() { // Virtual because it's designed to be subclassed...
+ Annotation *A = AnnotationList;
+ while (A) {
+ Annotation *Next = A->getNext();
+ delete A;
+ A = Next;
+ }
+}
+
+
typedef std::map<const std::string, unsigned> IDMapType;
static unsigned IDCounter = 0; // Unique ID counter
@@ -40,7 +52,6 @@
TheFactMap = 0;
}
}
-
AnnotationID AnnotationManager::getID(const std::string &Name) { // Name -> ID
IDMapType::iterator I = getIDMap().find(Name);
Index: llvm/lib/Support/LeakDetector.cpp
diff -u llvm/lib/Support/LeakDetector.cpp:1.6 llvm/lib/Support/LeakDetector.cpp:1.6.2.1
--- llvm/lib/Support/LeakDetector.cpp:1.6 Sun Dec 14 15:35:53 2003
+++ llvm/lib/Support/LeakDetector.cpp Mon Mar 1 17:58:13 2004
@@ -16,75 +16,95 @@
#include <set>
using namespace llvm;
-// Lazily allocate set so that release build doesn't have to do anything.
-static std::set<const void*> *Objects = 0;
-static std::set<const Value*> *LLVMObjects = 0;
-
-// Because the most common usage pattern, by far, is to add a garbage object,
-// then remove it immediately, we optimize this case. When an object is added,
-// it is not added to the set immediately, it is added to the CachedValue Value.
-// If it is immediately removed, no set search need be performed.
-//
-static const Value *CachedValue;
+namespace {
+ template <typename T>
+ struct LeakDetectorImpl {
+ LeakDetectorImpl(const char* const name) : Cache(0), Name(name) { }
+
+ // Because the most common usage pattern, by far, is to add a
+ // garbage object, then remove it immediately, we optimize this
+ // case. When an object is added, it is not added to the set
+ // immediately, it is added to the CachedValue Value. If it is
+ // immediately removed, no set search need be performed.
+ void addGarbage(const T* o) {
+ if (Cache) {
+ assert(Ts.count(Cache) == 0 && "Object already in set!");
+ Ts.insert(Cache);
+ }
+ Cache = o;
+ }
-void LeakDetector::addGarbageObjectImpl(void *Object) {
- if (Objects == 0)
- Objects = new std::set<const void*>();
- assert(Objects->count(Object) == 0 && "Object already in set!");
- Objects->insert(Object);
+ void removeGarbage(const T* o) {
+ if (o == Cache)
+ Cache = 0; // Cache hit
+ else
+ Ts.erase(o);
+ }
+
+ bool hasGarbage(const std::string& Message) {
+ addGarbage(0); // Flush the Cache
+
+ assert(Cache == 0 && "No value should be cached anymore!");
+
+ if (!Ts.empty()) {
+ std::cerr
+ << "Leaked " << Name << " objects found: " << Message << ":\n\t";
+ std::copy(Ts.begin(), Ts.end(),
+ std::ostream_iterator<const T*>(std::cerr, " "));
+ std::cerr << '\n';
+
+ // Clear out results so we don't get duplicate warnings on
+ // next call...
+ Ts.clear();
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ std::set<const T*> Ts;
+ const T* Cache;
+ const char* const Name;
+ };
+
+ typedef LeakDetectorImpl<void> Objects;
+ typedef LeakDetectorImpl<Value> LLVMObjects;
+
+ Objects& getObjects() {
+ static Objects *o = 0;
+ if (o == 0)
+ o = new Objects("GENERIC");
+ return *o;
+ }
+
+ LLVMObjects& getLLVMObjects() {
+ static LLVMObjects *o = 0;
+ if (o == 0)
+ o = new LLVMObjects("LLVM");
+ return *o;
+ }
}
-void LeakDetector::removeGarbageObjectImpl(void *Object) {
- if (Objects)
- Objects->erase(Object);
+void LeakDetector::addGarbageObjectImpl(void *Object) {
+ getObjects().addGarbage(Object);
}
void LeakDetector::addGarbageObjectImpl(const Value *Object) {
- if (CachedValue) {
- if (LLVMObjects == 0)
- LLVMObjects = new std::set<const Value*>();
- assert(LLVMObjects->count(CachedValue) == 0 && "Object already in set!");
- LLVMObjects->insert(CachedValue);
- }
- CachedValue = Object;
+ getLLVMObjects().addGarbage(Object);
+}
+
+void LeakDetector::removeGarbageObjectImpl(void *Object) {
+ getObjects().removeGarbage(Object);
}
void LeakDetector::removeGarbageObjectImpl(const Value *Object) {
- if (Object == CachedValue)
- CachedValue = 0; // Cache hit!
- else if (LLVMObjects)
- LLVMObjects->erase(Object);
+ getLLVMObjects().removeGarbage(Object);
}
void LeakDetector::checkForGarbageImpl(const std::string &Message) {
- if (CachedValue) // Flush the cache to the set...
- addGarbageObjectImpl((Value*)0);
-
- assert(CachedValue == 0 && "No value should be cached anymore!");
-
- if ((Objects && !Objects->empty()) || (LLVMObjects && !LLVMObjects->empty())){
- std::cerr << "Leaked objects found: " << Message << "\n";
-
- if (Objects && !Objects->empty()) {
- std::cerr << " Non-Value objects leaked:";
- for (std::set<const void*>::iterator I = Objects->begin(),
- E = Objects->end(); I != E; ++I)
- std::cerr << " " << *I;
- }
-
- if (LLVMObjects && !LLVMObjects->empty()) {
- std::cerr << " LLVM Value subclasses leaked:";
- for (std::set<const Value*>::iterator I = LLVMObjects->begin(),
- E = LLVMObjects->end(); I != E; ++I)
- std::cerr << **I << "\n";
- }
-
- std::cerr << "This is probably because you removed an LLVM value "
- << "(Instruction, BasicBlock, \netc), but didn't delete it. "
- << "Please check your code for memory leaks.\n";
-
- // Clear out results so we don't get duplicate warnings on next call...
- delete Objects; delete LLVMObjects;
- Objects = 0; LLVMObjects = 0;
- }
+ // use non-short-circuit version so that both checks are performed
+ if (getObjects().hasGarbage(Message) |
+ getLLVMObjects().hasGarbage(Message))
+ std::cerr << "\nThis is probably because you removed an object, but didn't "
+ "delete it. Please check your code for memory leaks.\n";
}
Index: llvm/lib/Support/Mangler.cpp
diff -u llvm/lib/Support/Mangler.cpp:1.8 llvm/lib/Support/Mangler.cpp:1.8.2.1
--- llvm/lib/Support/Mangler.cpp:1.8 Sun Dec 14 15:35:53 2003
+++ llvm/lib/Support/Mangler.cpp Mon Mar 1 17:58:13 2004
@@ -80,22 +80,37 @@
return name;
}
+void Mangler::InsertName(GlobalValue *GV,
+ std::map<std::string, GlobalValue*> &Names) {
+ if (!GV->hasName()) { // We must mangle unnamed globals.
+ MangledGlobals.insert(GV);
+ return;
+ }
+
+ // Figure out if this is already used.
+ GlobalValue *&ExistingValue = Names[GV->getName()];
+ if (!ExistingValue) {
+ ExistingValue = GV;
+ } else {
+ // If GV is external but the existing one is static, mangle the existing one
+ if (GV->hasExternalLinkage() && !ExistingValue->hasExternalLinkage()) {
+ MangledGlobals.insert(ExistingValue);
+ ExistingValue = GV;
+ } else {
+ // Otherwise, mangle GV
+ MangledGlobals.insert(GV);
+ }
+ }
+}
+
+
Mangler::Mangler(Module &m, bool addUnderscorePrefix)
- : M(m), AddUnderscorePrefix(addUnderscorePrefix) {
+ : M(m), AddUnderscorePrefix(addUnderscorePrefix), Count(0) {
// Calculate which global values have names that will collide when we throw
// away type information.
- std::set<std::string> FoundNames;
+ std::map<std::string, GlobalValue*> Names;
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
- if (I->hasName()) // If the global has a name...
- if (FoundNames.count(I->getName())) // And the name is already used
- MangledGlobals.insert(I); // Mangle the name
- else
- FoundNames.insert(I->getName()); // Otherwise, keep track of name
-
+ InsertName(I, Names);
for (Module::giterator I = M.gbegin(), E = M.gend(); I != E; ++I)
- if (I->hasName()) // If the global has a name...
- if (FoundNames.count(I->getName())) // And the name is already used
- MangledGlobals.insert(I); // Mangle the name
- else
- FoundNames.insert(I->getName()); // Otherwise, keep track of name
+ InsertName(I, Names);
}
Index: llvm/lib/Support/PluginLoader.cpp
diff -u llvm/lib/Support/PluginLoader.cpp:1.8 llvm/lib/Support/PluginLoader.cpp:1.8.2.1
--- llvm/lib/Support/PluginLoader.cpp:1.8 Sun Dec 14 15:35:53 2003
+++ llvm/lib/Support/PluginLoader.cpp Mon Mar 1 17:58:13 2004
@@ -37,5 +37,5 @@
// This causes operator= above to be invoked for every -load option.
static cl::opt<PluginLoader, false, cl::parser<std::string> >
-LoadOpt("load", cl::ZeroOrMore, cl::value_desc("plugin.so"),
+LoadOpt("load", cl::ZeroOrMore, cl::value_desc("plugin" SHLIBEXT),
cl::desc("Load the specified plugin"));
Index: llvm/lib/Support/Signals.cpp
diff -u llvm/lib/Support/Signals.cpp:1.11 llvm/lib/Support/Signals.cpp:1.11.2.1
--- llvm/lib/Support/Signals.cpp:1.11 Sun Dec 14 15:35:53 2003
+++ llvm/lib/Support/Signals.cpp Mon Mar 1 17:58:13 2004
@@ -17,8 +17,14 @@
#include <algorithm>
#include <cstdlib>
#include <cstdio>
-#include <signal.h>
#include "Config/config.h" // Get the signal handler return type
+#ifdef HAVE_EXECINFO_H
+# include <execinfo.h> // For backtrace().
+#endif
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <cerrno>
using namespace llvm;
static std::vector<std::string> FilesToRemove;
@@ -39,6 +45,65 @@
};
static const int *KillSigsEnd = KillSigs + sizeof(KillSigs)/sizeof(KillSigs[0]);
+#ifdef HAVE_BACKTRACE
+static void* StackTrace[256];
+#endif
+
+
+// PrintStackTrace - In the case of a program crash or fault, print out a stack
+// trace so that the user has an indication of why and where we died.
+//
+// On glibc systems we have the 'backtrace' function, which works nicely, but
+// doesn't demangle symbols. In order to backtrace symbols, we fork and exec a
+// 'c++filt' process to do the demangling. This seems like the simplest and
+// most robust solution when we can't allocate memory (such as in a signal
+// handler). If we can't find 'c++filt', we fallback to printing mangled names.
+//
+static void PrintStackTrace() {
+#ifdef HAVE_BACKTRACE
+ // Use backtrace() to output a backtrace on Linux systems with glibc.
+ int depth = backtrace(StackTrace, sizeof(StackTrace)/sizeof(StackTrace[0]));
+
+ // Create a one-way unix pipe. The backtracing process writes to PipeFDs[1],
+ // the c++filt process reads from PipeFDs[0].
+ int PipeFDs[2];
+ if (pipe(PipeFDs)) {
+ backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO);
+ return;
+ }
+
+ switch (pid_t ChildPID = fork()) {
+ case -1: // Error forking, print mangled stack trace
+ close(PipeFDs[0]);
+ close(PipeFDs[1]);
+ backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO);
+ return;
+ default: // backtracing process
+ close(PipeFDs[0]); // Close the reader side.
+
+ // Print the mangled backtrace into the pipe.
+ backtrace_symbols_fd(StackTrace, depth, PipeFDs[1]);
+ close(PipeFDs[1]); // We are done writing.
+ while (waitpid(ChildPID, 0, 0) == -1)
+ if (errno != EINTR) break;
+ return;
+
+ case 0: // c++filt process
+ close(PipeFDs[1]); // Close the writer side.
+ dup2(PipeFDs[0], 0); // Read from standard input
+ close(PipeFDs[0]); // Close the old descriptor
+ dup2(2, 1); // Revector stdout -> stderr
+
+ // Try to run c++filt or gc++filt. If neither is found, call back on 'cat'
+ // to print the mangled stack trace. If we can't find cat, just exit.
+ execlp("c++filt", "c++filt", 0);
+ execlp("gc++filt", "gc++filt", 0);
+ execlp("cat", "cat", 0);
+ execlp("/bin/cat", "cat", 0);
+ exit(0);
+ }
+#endif
+}
// SignalHandler - The signal handler that runs...
static RETSIGTYPE SignalHandler(int Sig) {
@@ -50,7 +115,9 @@
if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd)
exit(1); // If this is an interrupt signal, exit the program
- // Otherwise if it is a fault (like SEGV) reissue the signal to die...
+ // Otherwise if it is a fault (like SEGV) output the stacktrace to
+ // STDERR (if we can) and reissue the signal to die...
+ PrintStackTrace();
signal(Sig, SIG_DFL);
}
@@ -61,5 +128,11 @@
FilesToRemove.push_back(Filename);
std::for_each(IntSigs, IntSigsEnd, RegisterHandler);
+ std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
+}
+
+/// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or
+/// SIGSEGV) is delivered to the process, print a stack trace and then exit.
+void llvm::PrintStackTraceOnErrorSignal() {
std::for_each(KillSigs, KillSigsEnd, RegisterHandler);
}
Index: llvm/lib/Support/ToolRunner.cpp
diff -u llvm/lib/Support/ToolRunner.cpp:1.13 llvm/lib/Support/ToolRunner.cpp:1.13.2.1
--- llvm/lib/Support/ToolRunner.cpp:1.13 Sun Dec 14 15:35:53 2003
+++ llvm/lib/Support/ToolRunner.cpp Mon Mar 1 17:58:13 2004
@@ -18,8 +18,36 @@
#include "Support/FileUtilities.h"
#include <iostream>
#include <fstream>
+#include <sstream>
using namespace llvm;
+ToolExecutionError::~ToolExecutionError() throw() { }
+
+static void ProcessFailure(std::string ProgPath, const char** Args) {
+ std::ostringstream OS;
+ OS << "\nError running tool:\n ";
+ for (const char **Arg = Args; *Arg; ++Arg)
+ OS << " " << *Arg;
+ OS << "\n";
+
+ // Rerun the compiler, capturing any error messages to print them.
+ std::string ErrorFilename = getUniqueFilename("error_messages");
+ RunProgramWithTimeout(ProgPath, Args, "/dev/null", ErrorFilename.c_str(),
+ ErrorFilename.c_str());
+
+ // Print out the error messages generated by GCC if possible...
+ std::ifstream ErrorFile(ErrorFilename.c_str());
+ if (ErrorFile) {
+ std::copy(std::istreambuf_iterator<char>(ErrorFile),
+ std::istreambuf_iterator<char>(),
+ std::ostreambuf_iterator<char>(OS));
+ ErrorFile.close();
+ }
+
+ removeFile(ErrorFilename);
+ throw ToolExecutionError(OS.str());
+}
+
//===---------------------------------------------------------------------===//
// LLI Implementation of AbstractIntepreter interface
//
@@ -44,11 +72,9 @@
const std::string &InputFile,
const std::string &OutputFile,
const std::vector<std::string> &SharedLibs) {
- if (!SharedLibs.empty()) {
- std::cerr << "LLI currently does not support loading shared libraries.\n"
- << "Exiting.\n";
- exit(1);
- }
+ if (!SharedLibs.empty())
+ throw ToolExecutionError("LLI currently does not support "
+ "loading shared libraries.");
std::vector<const char*> LLIArgs;
LLIArgs.push_back(LLIPath.c_str());
@@ -86,7 +112,7 @@
//===----------------------------------------------------------------------===//
// LLC Implementation of AbstractIntepreter interface
//
-int LLC::OutputAsm(const std::string &Bytecode, std::string &OutputAsmFile) {
+void LLC::OutputAsm(const std::string &Bytecode, std::string &OutputAsmFile) {
OutputAsmFile = getUniqueFilename(Bytecode+".llc.s");
const char *LLCArgs[] = {
LLCPath.c_str(),
@@ -98,14 +124,14 @@
std::cout << "<llc>" << std::flush;
if (RunProgramWithTimeout(LLCPath, LLCArgs, "/dev/null", "/dev/null",
- "/dev/null")) {
- // If LLC failed on the bytecode, print error...
- std::cerr << "Error: `llc' failed!\n";
- removeFile(OutputAsmFile);
- return 1;
- }
+ "/dev/null"))
+ ProcessFailure(LLCPath, LLCArgs);
+}
- return 0;
+void LLC::compileProgram(const std::string &Bytecode) {
+ std::string OutputAsmFile;
+ OutputAsm(Bytecode, OutputAsmFile);
+ removeFile(OutputAsmFile);
}
int LLC::ExecuteProgram(const std::string &Bytecode,
@@ -115,16 +141,12 @@
const std::vector<std::string> &SharedLibs) {
std::string OutputAsmFile;
- if (OutputAsm(Bytecode, OutputAsmFile)) {
- std::cerr << "Could not generate asm code with `llc', exiting.\n";
- exit(1);
- }
+ OutputAsm(Bytecode, OutputAsmFile);
+ FileRemover OutFileRemover(OutputAsmFile);
// Assuming LLC worked, compile the result with GCC and run it.
- int Result = gcc->ExecuteProgram(OutputAsmFile, Args, GCC::AsmFile,
- InputFile, OutputFile, SharedLibs);
- removeFile(OutputAsmFile);
- return Result;
+ return gcc->ExecuteProgram(OutputAsmFile, Args, GCC::AsmFile,
+ InputFile, OutputFile, SharedLibs);
}
/// createLLC - Try to find the LLC executable
@@ -211,27 +233,28 @@
return 0;
}
-int CBE::OutputC(const std::string &Bytecode,
+void CBE::OutputC(const std::string &Bytecode,
std::string &OutputCFile) {
OutputCFile = getUniqueFilename(Bytecode+".cbe.c");
- const char *DisArgs[] = {
- DISPath.c_str(),
+ const char *LLCArgs[] = {
+ LLCPath.c_str(),
"-o", OutputCFile.c_str(), // Output to the C file
- "-c", // Output to C
+ "-march=c", // Output to C
"-f", // Overwrite as necessary...
Bytecode.c_str(), // This is the input bytecode
0
};
std::cout << "<cbe>" << std::flush;
- if (RunProgramWithTimeout(DISPath, DisArgs, "/dev/null", "/dev/null",
- "/dev/null")) {
- // If dis failed on the bytecode, print error...
- std::cerr << "Error: `llvm-dis -c' failed!\n";
- return 1;
- }
+ if (RunProgramWithTimeout(LLCPath, LLCArgs, "/dev/null", "/dev/null",
+ "/dev/null"))
+ ProcessFailure(LLCPath, LLCArgs);
+}
- return 0;
+void CBE::compileProgram(const std::string &Bytecode) {
+ std::string OutputCFile;
+ OutputC(Bytecode, OutputCFile);
+ removeFile(OutputCFile);
}
int CBE::ExecuteProgram(const std::string &Bytecode,
@@ -240,36 +263,32 @@
const std::string &OutputFile,
const std::vector<std::string> &SharedLibs) {
std::string OutputCFile;
- if (OutputC(Bytecode, OutputCFile)) {
- std::cerr << "Could not generate C code with `llvm-dis', exiting.\n";
- exit(1);
- }
+ OutputC(Bytecode, OutputCFile);
- int Result = gcc->ExecuteProgram(OutputCFile, Args, GCC::CFile,
- InputFile, OutputFile, SharedLibs);
- removeFile(OutputCFile);
+ FileRemover CFileRemove(OutputCFile);
- return Result;
+ return gcc->ExecuteProgram(OutputCFile, Args, GCC::CFile,
+ InputFile, OutputFile, SharedLibs);
}
-/// createCBE - Try to find the 'llvm-dis' executable
+/// createCBE - Try to find the 'llc' executable
///
CBE *AbstractInterpreter::createCBE(const std::string &ProgramPath,
std::string &Message) {
- std::string DISPath = FindExecutable("llvm-dis", ProgramPath);
- if (DISPath.empty()) {
+ std::string LLCPath = FindExecutable("llc", ProgramPath);
+ if (LLCPath.empty()) {
Message =
- "Cannot find `llvm-dis' in executable directory or PATH!\n";
+ "Cannot find `llc' in executable directory or PATH!\n";
return 0;
}
- Message = "Found llvm-dis: " + DISPath + "\n";
+ Message = "Found llc: " + LLCPath + "\n";
GCC *gcc = GCC::create(ProgramPath, Message);
if (!gcc) {
std::cerr << Message << "\n";
exit(1);
}
- return new CBE(DISPath, gcc);
+ return new CBE(LLCPath, gcc);
}
//===---------------------------------------------------------------------===//
@@ -311,7 +330,7 @@
std::cout << "<gcc>" << std::flush;
if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], "/dev/null", "/dev/null",
"/dev/null")) {
- ProcessFailure(&GCCArgs[0]);
+ ProcessFailure(GCCPath, &GCCArgs[0]);
exit(1);
}
@@ -329,15 +348,15 @@
std::cerr << " " << ProgramArgs[i];
std::cerr << "\n";
);
- int ProgramResult = RunProgramWithTimeout(OutputBinary, &ProgramArgs[0],
- InputFile, OutputFile, OutputFile);
- removeFile(OutputBinary);
- return ProgramResult;
+
+ FileRemover OutputBinaryRemover(OutputBinary);
+ return RunProgramWithTimeout(OutputBinary, &ProgramArgs[0],
+ InputFile, OutputFile, OutputFile);
}
int GCC::MakeSharedObject(const std::string &InputFile, FileType fileType,
std::string &OutputFile) {
- OutputFile = getUniqueFilename(InputFile+".so");
+ OutputFile = getUniqueFilename(InputFile+SHLIBEXT);
// Compile the C/asm file into a shared object
const char* GCCArgs[] = {
GCCPath.c_str(),
@@ -357,34 +376,10 @@
std::cout << "<gcc>" << std::flush;
if (RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null",
"/dev/null")) {
- ProcessFailure(GCCArgs);
+ ProcessFailure(GCCPath, GCCArgs);
return 1;
}
return 0;
-}
-
-void GCC::ProcessFailure(const char** GCCArgs) {
- std::cerr << "\n*** Error: invocation of the C compiler failed!\n";
- for (const char **Arg = GCCArgs; *Arg; ++Arg)
- std::cerr << " " << *Arg;
- std::cerr << "\n";
-
- // Rerun the compiler, capturing any error messages to print them.
- std::string ErrorFilename = getUniqueFilename("gcc.errors");
- RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", ErrorFilename.c_str(),
- ErrorFilename.c_str());
-
- // Print out the error messages generated by GCC if possible...
- std::ifstream ErrorFile(ErrorFilename.c_str());
- if (ErrorFile) {
- std::copy(std::istreambuf_iterator<char>(ErrorFile),
- std::istreambuf_iterator<char>(),
- std::ostreambuf_iterator<char>(std::cerr));
- ErrorFile.close();
- std::cerr << "\n";
- }
-
- removeFile(ErrorFilename);
}
/// create - Try to find the `gcc' executable
More information about the llvm-commits
mailing list