Home | History | Annotate | Download | only in bugpoint
      1 //===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file defines an interface that allows bugpoint to run various passes
     11 // without the threat of a buggy pass corrupting bugpoint (of course, bugpoint
     12 // may have its own bugs, but that's another story...).  It achieves this by
     13 // forking a copy of itself and having the child process do the optimizations.
     14 // If this client dies, we can always fork a new one.  :)
     15 //
     16 //===----------------------------------------------------------------------===//
     17 
     18 #include "BugDriver.h"
     19 #include "llvm/Analysis/Verifier.h"
     20 #include "llvm/Bitcode/ReaderWriter.h"
     21 #include "llvm/IR/DataLayout.h"
     22 #include "llvm/IR/Module.h"
     23 #include "llvm/PassManager.h"
     24 #include "llvm/Support/CommandLine.h"
     25 #include "llvm/Support/Debug.h"
     26 #include "llvm/Support/FileUtilities.h"
     27 #include "llvm/Support/Path.h"
     28 #include "llvm/Support/Program.h"
     29 #include "llvm/Support/SystemUtils.h"
     30 #include "llvm/Support/ToolOutputFile.h"
     31 
     32 #define DONT_GET_PLUGIN_LOADER_OPTION
     33 #include "llvm/Support/PluginLoader.h"
     34 
     35 #include <fstream>
     36 
     37 using namespace llvm;
     38 
     39 namespace llvm {
     40   extern cl::opt<std::string> OutputPrefix;
     41 }
     42 
     43 namespace {
     44   // ChildOutput - This option captures the name of the child output file that
     45   // is set up by the parent bugpoint process
     46   cl::opt<std::string> ChildOutput("child-output", cl::ReallyHidden);
     47   cl::opt<std::string> OptCmd("opt-command", cl::init(""),
     48                               cl::desc("Path to opt. (default: search path "
     49                                        "for 'opt'.)"));
     50 }
     51 
     52 /// writeProgramToFile - This writes the current "Program" to the named bitcode
     53 /// file.  If an error occurs, true is returned.
     54 ///
     55 static bool writeProgramToFileAux(tool_output_file &Out, const Module *M) {
     56   WriteBitcodeToFile(M, Out.os());
     57   Out.os().close();
     58   if (!Out.os().has_error()) {
     59     Out.keep();
     60     return false;
     61   }
     62   return true;
     63 }
     64 
     65 bool BugDriver::writeProgramToFile(const std::string &Filename, int FD,
     66                                    const Module *M) const {
     67   tool_output_file Out(Filename.c_str(), FD);
     68   return writeProgramToFileAux(Out, M);
     69 }
     70 
     71 bool BugDriver::writeProgramToFile(const std::string &Filename,
     72                                    const Module *M) const {
     73   std::string ErrInfo;
     74   tool_output_file Out(Filename.c_str(), ErrInfo, sys::fs::F_Binary);
     75   if (ErrInfo.empty())
     76     return writeProgramToFileAux(Out, M);
     77   return true;
     78 }
     79 
     80 
     81 /// EmitProgressBitcode - This function is used to output the current Program
     82 /// to a file named "bugpoint-ID.bc".
     83 ///
     84 void BugDriver::EmitProgressBitcode(const Module *M,
     85                                     const std::string &ID,
     86                                     bool NoFlyer)  const {
     87   // Output the input to the current pass to a bitcode file, emit a message
     88   // telling the user how to reproduce it: opt -foo blah.bc
     89   //
     90   std::string Filename = OutputPrefix + "-" + ID + ".bc";
     91   if (writeProgramToFile(Filename, M)) {
     92     errs() <<  "Error opening file '" << Filename << "' for writing!\n";
     93     return;
     94   }
     95 
     96   outs() << "Emitted bitcode to '" << Filename << "'\n";
     97   if (NoFlyer || PassesToRun.empty()) return;
     98   outs() << "\n*** You can reproduce the problem with: ";
     99   if (UseValgrind) outs() << "valgrind ";
    100   outs() << "opt " << Filename;
    101   for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
    102     outs() << " -load " << PluginLoader::getPlugin(i);
    103   }
    104   outs() << " " << getPassesString(PassesToRun) << "\n";
    105 }
    106 
    107 cl::opt<bool> SilencePasses("silence-passes",
    108         cl::desc("Suppress output of running passes (both stdout and stderr)"));
    109 
    110 static cl::list<std::string> OptArgs("opt-args", cl::Positional,
    111                                      cl::desc("<opt arguments>..."),
    112                                      cl::ZeroOrMore, cl::PositionalEatsArgs);
    113 
    114 /// runPasses - Run the specified passes on Program, outputting a bitcode file
    115 /// and writing the filename into OutputFile if successful.  If the
    116 /// optimizations fail for some reason (optimizer crashes), return true,
    117 /// otherwise return false.  If DeleteOutput is set to true, the bitcode is
    118 /// deleted on success, and the filename string is undefined.  This prints to
    119 /// outs() a single line message indicating whether compilation was successful
    120 /// or failed.
    121 ///
    122 bool BugDriver::runPasses(Module *Program,
    123                           const std::vector<std::string> &Passes,
    124                           std::string &OutputFilename, bool DeleteOutput,
    125                           bool Quiet, unsigned NumExtraArgs,
    126                           const char * const *ExtraArgs) const {
    127   // setup the output file name
    128   outs().flush();
    129   SmallString<128> UniqueFilename;
    130   error_code EC = sys::fs::createUniqueFile(
    131       OutputPrefix + "-output-%%%%%%%.bc", UniqueFilename);
    132   if (EC) {
    133     errs() << getToolName() << ": Error making unique filename: "
    134            << EC.message() << "\n";
    135     return 1;
    136   }
    137   OutputFilename = UniqueFilename.str();
    138 
    139   // set up the input file name
    140   SmallString<128> InputFilename;
    141   int InputFD;
    142   EC = sys::fs::createUniqueFile(OutputPrefix + "-input-%%%%%%%.bc", InputFD,
    143                                  InputFilename);
    144   if (EC) {
    145     errs() << getToolName() << ": Error making unique filename: "
    146            << EC.message() << "\n";
    147     return 1;
    148   }
    149 
    150   tool_output_file InFile(InputFilename.c_str(), InputFD);
    151 
    152   WriteBitcodeToFile(Program, InFile.os());
    153   InFile.os().close();
    154   if (InFile.os().has_error()) {
    155     errs() << "Error writing bitcode file: " << InputFilename << "\n";
    156     InFile.os().clear_error();
    157     return 1;
    158   }
    159 
    160   std::string tool = OptCmd.empty()? sys::FindProgramByName("opt") : OptCmd;
    161   if (tool.empty()) {
    162     errs() << "Cannot find `opt' in PATH!\n";
    163     return 1;
    164   }
    165 
    166   // Ok, everything that could go wrong before running opt is done.
    167   InFile.keep();
    168 
    169   // setup the child process' arguments
    170   SmallVector<const char*, 8> Args;
    171   if (UseValgrind) {
    172     Args.push_back("valgrind");
    173     Args.push_back("--error-exitcode=1");
    174     Args.push_back("-q");
    175     Args.push_back(tool.c_str());
    176   } else
    177     Args.push_back(tool.c_str());
    178 
    179   Args.push_back("-o");
    180   Args.push_back(OutputFilename.c_str());
    181   for (unsigned i = 0, e = OptArgs.size(); i != e; ++i)
    182     Args.push_back(OptArgs[i].c_str());
    183   std::vector<std::string> pass_args;
    184   for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
    185     pass_args.push_back( std::string("-load"));
    186     pass_args.push_back( PluginLoader::getPlugin(i));
    187   }
    188   for (std::vector<std::string>::const_iterator I = Passes.begin(),
    189        E = Passes.end(); I != E; ++I )
    190     pass_args.push_back( std::string("-") + (*I) );
    191   for (std::vector<std::string>::const_iterator I = pass_args.begin(),
    192        E = pass_args.end(); I != E; ++I )
    193     Args.push_back(I->c_str());
    194   Args.push_back(InputFilename.c_str());
    195   for (unsigned i = 0; i < NumExtraArgs; ++i)
    196     Args.push_back(*ExtraArgs);
    197   Args.push_back(0);
    198 
    199   DEBUG(errs() << "\nAbout to run:\t";
    200         for (unsigned i = 0, e = Args.size()-1; i != e; ++i)
    201           errs() << " " << Args[i];
    202         errs() << "\n";
    203         );
    204 
    205   std::string Prog;
    206   if (UseValgrind)
    207     Prog = sys::FindProgramByName("valgrind");
    208   else
    209     Prog = tool;
    210 
    211   // Redirect stdout and stderr to nowhere if SilencePasses is given
    212   StringRef Nowhere;
    213   const StringRef *Redirects[3] = {0, &Nowhere, &Nowhere};
    214 
    215   std::string ErrMsg;
    216   int result = sys::ExecuteAndWait(Prog, Args.data(), 0,
    217                                    (SilencePasses ? Redirects : 0), Timeout,
    218                                    MemoryLimit, &ErrMsg);
    219 
    220   // If we are supposed to delete the bitcode file or if the passes crashed,
    221   // remove it now.  This may fail if the file was never created, but that's ok.
    222   if (DeleteOutput || result != 0)
    223     sys::fs::remove(OutputFilename);
    224 
    225   // Remove the temporary input file as well
    226   sys::fs::remove(InputFilename.c_str());
    227 
    228   if (!Quiet) {
    229     if (result == 0)
    230       outs() << "Success!\n";
    231     else if (result > 0)
    232       outs() << "Exited with error code '" << result << "'\n";
    233     else if (result < 0) {
    234       if (result == -1)
    235         outs() << "Execute failed: " << ErrMsg << "\n";
    236       else
    237         outs() << "Crashed: " << ErrMsg << "\n";
    238     }
    239     if (result & 0x01000000)
    240       outs() << "Dumped core\n";
    241   }
    242 
    243   // Was the child successful?
    244   return result != 0;
    245 }
    246 
    247 
    248 /// runPassesOn - Carefully run the specified set of pass on the specified
    249 /// module, returning the transformed module on success, or a null pointer on
    250 /// failure.
    251 Module *BugDriver::runPassesOn(Module *M,
    252                                const std::vector<std::string> &Passes,
    253                                bool AutoDebugCrashes, unsigned NumExtraArgs,
    254                                const char * const *ExtraArgs) {
    255   std::string BitcodeResult;
    256   if (runPasses(M, Passes, BitcodeResult, false/*delete*/, true/*quiet*/,
    257                 NumExtraArgs, ExtraArgs)) {
    258     if (AutoDebugCrashes) {
    259       errs() << " Error running this sequence of passes"
    260              << " on the input program!\n";
    261       delete swapProgramIn(M);
    262       EmitProgressBitcode(M, "pass-error",  false);
    263       exit(debugOptimizerCrash());
    264     }
    265     return 0;
    266   }
    267 
    268   Module *Ret = ParseInputFile(BitcodeResult, Context);
    269   if (Ret == 0) {
    270     errs() << getToolName() << ": Error reading bitcode file '"
    271            << BitcodeResult << "'!\n";
    272     exit(1);
    273   }
    274   sys::fs::remove(BitcodeResult);
    275   return Ret;
    276 }
    277