ExecutionDriver.cpp revision f1b20d8620b05abaa52f40ac6d21f839b265fb00
1//===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file was developed by the LLVM research group and is distributed under 6// the University of Illinois Open Source License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file contains code used to execute the program utilizing one of the 11// various ways of running LLVM bytecode. 12// 13//===----------------------------------------------------------------------===// 14 15#include "BugDriver.h" 16#include "ToolRunner.h" 17#include "llvm/Support/CommandLine.h" 18#include "llvm/Support/Debug.h" 19#include "llvm/Support/FileUtilities.h" 20#include "llvm/Support/SystemUtils.h" 21#include <fstream> 22#include <iostream> 23 24using namespace llvm; 25 26namespace { 27 // OutputType - Allow the user to specify the way code should be run, to test 28 // for miscompilation. 29 // 30 enum OutputType { 31 AutoPick, RunLLI, RunJIT, RunLLC, RunCBE 32 }; 33 34 cl::opt<double> 35 AbsTolerance("abs-tolerance", cl::desc("Absolute error tolerated"), 36 cl::init(0.0)); 37 cl::opt<double> 38 RelTolerance("rel-tolerance", cl::desc("Relative error tolerated"), 39 cl::init(0.0)); 40 41 cl::opt<OutputType> 42 InterpreterSel(cl::desc("Specify how LLVM code should be executed:"), 43 cl::values(clEnumValN(AutoPick, "auto", "Use best guess"), 44 clEnumValN(RunLLI, "run-int", 45 "Execute with the interpreter"), 46 clEnumValN(RunJIT, "run-jit", "Execute with JIT"), 47 clEnumValN(RunLLC, "run-llc", "Compile with LLC"), 48 clEnumValN(RunCBE, "run-cbe", "Compile with CBE"), 49 clEnumValEnd), 50 cl::init(AutoPick)); 51 52 cl::opt<bool> 53 CheckProgramExitCode("check-exit-code", 54 cl::desc("Assume nonzero exit code is failure (default on)"), 55 cl::init(true)); 56 57 cl::opt<std::string> 58 InputFile("input", cl::init("/dev/null"), 59 cl::desc("Filename to pipe in as stdin (default: /dev/null)")); 60 61 cl::list<std::string> 62 AdditionalSOs("additional-so", 63 cl::desc("Additional shared objects to load " 64 "into executing programs")); 65 66 cl::opt<unsigned> 67 TimeoutValue("timeout", cl::init(300), cl::value_desc("seconds"), 68 cl::desc("Number of seconds program is allowed to run before it " 69 "is killed (default is 300s), 0 disables timeout")); 70 71 cl::list<std::string> 72 AdditionalLinkerArgs("Xlinker", 73 cl::desc("Additional arguments to pass to the linker")); 74} 75 76namespace llvm { 77 // Anything specified after the --args option are taken as arguments to the 78 // program being debugged. 79 cl::list<std::string> 80 InputArgv("args", cl::Positional, cl::desc("<program arguments>..."), 81 cl::ZeroOrMore, cl::PositionalEatsArgs); 82 83 cl::list<std::string> 84 ToolArgv("tool-args", cl::Positional, cl::desc("<tool arguments>..."), 85 cl::ZeroOrMore, cl::PositionalEatsArgs); 86} 87 88//===----------------------------------------------------------------------===// 89// BugDriver method implementation 90// 91 92/// initializeExecutionEnvironment - This method is used to set up the 93/// environment for executing LLVM programs. 94/// 95bool BugDriver::initializeExecutionEnvironment() { 96 std::cout << "Initializing execution environment: "; 97 98 // Create an instance of the AbstractInterpreter interface as specified on 99 // the command line 100 cbe = 0; 101 std::string Message; 102 103 switch (InterpreterSel) { 104 case AutoPick: 105 InterpreterSel = RunCBE; 106 Interpreter = cbe = AbstractInterpreter::createCBE(getToolName(), Message, 107 &ToolArgv); 108 if (!Interpreter) { 109 InterpreterSel = RunJIT; 110 Interpreter = AbstractInterpreter::createJIT(getToolName(), Message, 111 &ToolArgv); 112 } 113 if (!Interpreter) { 114 InterpreterSel = RunLLC; 115 Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, 116 &ToolArgv); 117 } 118 if (!Interpreter) { 119 InterpreterSel = RunLLI; 120 Interpreter = AbstractInterpreter::createLLI(getToolName(), Message, 121 &ToolArgv); 122 } 123 if (!Interpreter) { 124 InterpreterSel = AutoPick; 125 Message = "Sorry, I can't automatically select an interpreter!\n"; 126 } 127 break; 128 case RunLLI: 129 Interpreter = AbstractInterpreter::createLLI(getToolName(), Message, 130 &ToolArgv); 131 break; 132 case RunLLC: 133 Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, 134 &ToolArgv); 135 break; 136 case RunJIT: 137 Interpreter = AbstractInterpreter::createJIT(getToolName(), Message, 138 &ToolArgv); 139 break; 140 case RunCBE: 141 Interpreter = cbe = AbstractInterpreter::createCBE(getToolName(), Message, 142 &ToolArgv); 143 break; 144 default: 145 Message = "Sorry, this back-end is not supported by bugpoint right now!\n"; 146 break; 147 } 148 std::cerr << Message; 149 150 // Initialize auxiliary tools for debugging 151 if (!cbe) { 152 cbe = AbstractInterpreter::createCBE(getToolName(), Message, &ToolArgv); 153 if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); } 154 } 155 gcc = GCC::create(getToolName(), Message); 156 if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); } 157 158 // If there was an error creating the selected interpreter, quit with error. 159 return Interpreter == 0; 160} 161 162/// compileProgram - Try to compile the specified module, throwing an exception 163/// if an error occurs, or returning normally if not. This is used for code 164/// generation crash testing. 165/// 166void BugDriver::compileProgram(Module *M) { 167 // Emit the program to a bytecode file... 168 sys::Path BytecodeFile ("bugpoint-test-program.bc"); 169 BytecodeFile.makeUnique(); 170 if (writeProgramToFile(BytecodeFile.toString(), M)) { 171 std::cerr << ToolName << ": Error emitting bytecode to file '" 172 << BytecodeFile << "'!\n"; 173 exit(1); 174 } 175 176 // Remove the temporary bytecode file when we are done. 177 FileRemover BytecodeFileRemover(BytecodeFile); 178 179 // Actually compile the program! 180 Interpreter->compileProgram(BytecodeFile.toString()); 181} 182 183 184/// executeProgram - This method runs "Program", capturing the output of the 185/// program to a file, returning the filename of the file. A recommended 186/// filename may be optionally specified. 187/// 188std::string BugDriver::executeProgram(std::string OutputFile, 189 std::string BytecodeFile, 190 const std::string &SharedObj, 191 AbstractInterpreter *AI, 192 bool *ProgramExitedNonzero) { 193 if (AI == 0) AI = Interpreter; 194 assert(AI && "Interpreter should have been created already!"); 195 bool CreatedBytecode = false; 196 if (BytecodeFile.empty()) { 197 // Emit the program to a bytecode file... 198 sys::Path uniqueFilename("bugpoint-test-program.bc"); 199 uniqueFilename.makeUnique(); 200 BytecodeFile = uniqueFilename.toString(); 201 202 if (writeProgramToFile(BytecodeFile, Program)) { 203 std::cerr << ToolName << ": Error emitting bytecode to file '" 204 << BytecodeFile << "'!\n"; 205 exit(1); 206 } 207 CreatedBytecode = true; 208 } 209 210 // Remove the temporary bytecode file when we are done. 211 sys::Path BytecodePath (BytecodeFile); 212 FileRemover BytecodeFileRemover(BytecodePath, CreatedBytecode); 213 214 if (OutputFile.empty()) OutputFile = "bugpoint-execution-output"; 215 216 // Check to see if this is a valid output filename... 217 sys::Path uniqueFile(OutputFile); 218 uniqueFile.makeUnique(); 219 OutputFile = uniqueFile.toString(); 220 221 // Figure out which shared objects to run, if any. 222 std::vector<std::string> SharedObjs(AdditionalSOs); 223 if (!SharedObj.empty()) 224 SharedObjs.push_back(SharedObj); 225 226 227 // If this is an LLC or CBE run, then the GCC compiler might get run to 228 // compile the program. If so, we should pass the user's -Xlinker options 229 // as the GCCArgs. 230 int RetVal = 0; 231 if (InterpreterSel == RunLLC || InterpreterSel == RunCBE) 232 RetVal = AI->ExecuteProgram(BytecodeFile, InputArgv, InputFile, 233 OutputFile, AdditionalLinkerArgs, SharedObjs, 234 TimeoutValue); 235 else 236 RetVal = AI->ExecuteProgram(BytecodeFile, InputArgv, InputFile, 237 OutputFile, std::vector<std::string>(), 238 SharedObjs, TimeoutValue); 239 240 if (RetVal == -1) { 241 std::cerr << "<timeout>"; 242 static bool FirstTimeout = true; 243 if (FirstTimeout) { 244 std::cout << "\n" 245 "*** Program execution timed out! This mechanism is designed to handle\n" 246 " programs stuck in infinite loops gracefully. The -timeout option\n" 247 " can be used to change the timeout threshold or disable it completely\n" 248 " (with -timeout=0). This message is only displayed once.\n"; 249 FirstTimeout = false; 250 } 251 } 252 253 if (ProgramExitedNonzero != 0) 254 *ProgramExitedNonzero = (RetVal != 0); 255 256 // Return the filename we captured the output to. 257 return OutputFile; 258} 259 260/// executeProgramWithCBE - Used to create reference output with the C 261/// backend, if reference output is not provided. 262/// 263std::string BugDriver::executeProgramWithCBE(std::string OutputFile) { 264 bool ProgramExitedNonzero; 265 std::string outFN = executeProgram(OutputFile, "", "", 266 (AbstractInterpreter*)cbe, 267 &ProgramExitedNonzero); 268 if (ProgramExitedNonzero) { 269 std::cerr 270 << "Warning: While generating reference output, program exited with\n" 271 << "non-zero exit code. This will NOT be treated as a failure.\n"; 272 CheckProgramExitCode = false; 273 } 274 return outFN; 275} 276 277std::string BugDriver::compileSharedObject(const std::string &BytecodeFile) { 278 assert(Interpreter && "Interpreter should have been created already!"); 279 sys::Path OutputCFile; 280 281 // Using CBE 282 cbe->OutputC(BytecodeFile, OutputCFile); 283 284#if 0 /* This is an alternative, as yet unimplemented */ 285 // Using LLC 286 std::string Message; 287 LLC *llc = createLLCtool(Message); 288 if (llc->OutputAsm(BytecodeFile, OutputFile)) { 289 std::cerr << "Could not generate asm code with `llc', exiting.\n"; 290 exit(1); 291 } 292#endif 293 294 std::string SharedObjectFile; 295 if (gcc->MakeSharedObject(OutputCFile.toString(), GCC::CFile, 296 SharedObjectFile)) 297 exit(1); 298 299 // Remove the intermediate C file 300 OutputCFile.eraseFromDisk(); 301 302 return "./" + SharedObjectFile; 303} 304 305 306/// diffProgram - This method executes the specified module and diffs the output 307/// against the file specified by ReferenceOutputFile. If the output is 308/// different, true is returned. 309/// 310bool BugDriver::diffProgram(const std::string &BytecodeFile, 311 const std::string &SharedObject, 312 bool RemoveBytecode) { 313 bool ProgramExitedNonzero; 314 315 // Execute the program, generating an output file... 316 sys::Path Output (executeProgram("", BytecodeFile, SharedObject, 0, 317 &ProgramExitedNonzero)); 318 319 // If we're checking the program exit code, assume anything nonzero is bad. 320 if (CheckProgramExitCode && ProgramExitedNonzero) { 321 Output.eraseFromDisk(); 322 if (RemoveBytecode) 323 sys::Path(BytecodeFile).eraseFromDisk(); 324 return true; 325 } 326 327 std::string Error; 328 bool FilesDifferent = false; 329 if (int Diff = DiffFilesWithTolerance(sys::Path(ReferenceOutputFile), 330 sys::Path(Output.toString()), 331 AbsTolerance, RelTolerance, &Error)) { 332 if (Diff == 2) { 333 std::cerr << "While diffing output: " << Error << '\n'; 334 exit(1); 335 } 336 FilesDifferent = true; 337 } 338 339 // Remove the generated output. 340 Output.eraseFromDisk(); 341 342 // Remove the bytecode file if we are supposed to. 343 if (RemoveBytecode) 344 sys::Path(BytecodeFile).eraseFromDisk(); 345 return FilesDifferent; 346} 347 348bool BugDriver::isExecutingJIT() { 349 return InterpreterSel == RunJIT; 350} 351 352