ExecutionDriver.cpp revision d41b30def3181bce4bf87e8bde664d15663165d0
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, CBE_bug 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 clEnumValN(CBE_bug,"cbe-bug", "Find CBE bugs"), 50 clEnumValEnd), 51 cl::init(AutoPick)); 52 53 cl::opt<bool> 54 CheckProgramExitCode("check-exit-code", 55 cl::desc("Assume nonzero exit code is failure (default on)"), 56 cl::init(true)); 57 58 cl::opt<std::string> 59 InputFile("input", cl::init("/dev/null"), 60 cl::desc("Filename to pipe in as stdin (default: /dev/null)")); 61 62 cl::list<std::string> 63 AdditionalSOs("additional-so", 64 cl::desc("Additional shared objects to load " 65 "into executing programs")); 66 67 cl::list<std::string> 68 AdditionalLinkerArgs("Xlinker", 69 cl::desc("Additional arguments to pass to the linker")); 70} 71 72namespace llvm { 73 // Anything specified after the --args option are taken as arguments to the 74 // program being debugged. 75 cl::list<std::string> 76 InputArgv("args", cl::Positional, cl::desc("<program arguments>..."), 77 cl::ZeroOrMore, cl::PositionalEatsArgs); 78 79 cl::list<std::string> 80 ToolArgv("tool-args", cl::Positional, cl::desc("<tool arguments>..."), 81 cl::ZeroOrMore, cl::PositionalEatsArgs); 82} 83 84//===----------------------------------------------------------------------===// 85// BugDriver method implementation 86// 87 88/// initializeExecutionEnvironment - This method is used to set up the 89/// environment for executing LLVM programs. 90/// 91bool BugDriver::initializeExecutionEnvironment() { 92 std::cout << "Initializing execution environment: "; 93 94 // Create an instance of the AbstractInterpreter interface as specified on 95 // the command line 96 cbe = 0; 97 std::string Message; 98 99 switch (InterpreterSel) { 100 case AutoPick: 101 InterpreterSel = RunCBE; 102 Interpreter = cbe = AbstractInterpreter::createCBE(getToolName(), Message, 103 &ToolArgv); 104 if (!Interpreter) { 105 InterpreterSel = RunJIT; 106 Interpreter = AbstractInterpreter::createJIT(getToolName(), Message, 107 &ToolArgv); 108 } 109 if (!Interpreter) { 110 InterpreterSel = RunLLC; 111 Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, 112 &ToolArgv); 113 } 114 if (!Interpreter) { 115 InterpreterSel = RunLLI; 116 Interpreter = AbstractInterpreter::createLLI(getToolName(), Message, 117 &ToolArgv); 118 } 119 if (!Interpreter) { 120 InterpreterSel = AutoPick; 121 Message = "Sorry, I can't automatically select an interpreter!\n"; 122 } 123 break; 124 case RunLLI: 125 Interpreter = AbstractInterpreter::createLLI(getToolName(), Message, 126 &ToolArgv); 127 break; 128 case RunLLC: 129 Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, 130 &ToolArgv); 131 break; 132 case RunJIT: 133 Interpreter = AbstractInterpreter::createJIT(getToolName(), Message, 134 &ToolArgv); 135 break; 136 case RunCBE: 137 case CBE_bug: 138 Interpreter = AbstractInterpreter::createCBE(getToolName(), Message, 139 &ToolArgv); 140 break; 141 default: 142 Message = "Sorry, this back-end is not supported by bugpoint right now!\n"; 143 break; 144 } 145 std::cerr << Message; 146 147 // Initialize auxiliary tools for debugging 148 if (InterpreterSel == RunCBE) { 149 // We already created a CBE, reuse it. 150 cbe = Interpreter; 151 } else if (InterpreterSel == CBE_bug) { 152 // We want to debug the CBE itself. Use LLC as the 'known-good' compiler. 153 std::vector<std::string> ToolArgs; 154 ToolArgs.push_back("--relocation-model=pic"); 155 cbe = AbstractInterpreter::createLLC(getToolName(), Message, &ToolArgs); 156 } else { 157 cbe = AbstractInterpreter::createCBE(getToolName(), Message, &ToolArgv); 158 } 159 if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); } 160 161 gcc = GCC::create(getToolName(), Message); 162 if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); } 163 164 // If there was an error creating the selected interpreter, quit with error. 165 return Interpreter == 0; 166} 167 168/// compileProgram - Try to compile the specified module, throwing an exception 169/// if an error occurs, or returning normally if not. This is used for code 170/// generation crash testing. 171/// 172void BugDriver::compileProgram(Module *M) { 173 // Emit the program to a bytecode file... 174 sys::Path BytecodeFile ("bugpoint-test-program.bc"); 175 std::string ErrMsg; 176 if (BytecodeFile.makeUnique(true,&ErrMsg)) { 177 std::cerr << ToolName << ": Error making unique filename: " << ErrMsg 178 << "\n"; 179 exit(1); 180 } 181 if (writeProgramToFile(BytecodeFile.toString(), M)) { 182 std::cerr << ToolName << ": Error emitting bytecode to file '" 183 << BytecodeFile << "'!\n"; 184 exit(1); 185 } 186 187 // Remove the temporary bytecode file when we are done. 188 FileRemover BytecodeFileRemover(BytecodeFile); 189 190 // Actually compile the program! 191 Interpreter->compileProgram(BytecodeFile.toString()); 192} 193 194 195/// executeProgram - This method runs "Program", capturing the output of the 196/// program to a file, returning the filename of the file. A recommended 197/// filename may be optionally specified. 198/// 199std::string BugDriver::executeProgram(std::string OutputFile, 200 std::string BytecodeFile, 201 const std::string &SharedObj, 202 AbstractInterpreter *AI, 203 bool *ProgramExitedNonzero) { 204 if (AI == 0) AI = Interpreter; 205 assert(AI && "Interpreter should have been created already!"); 206 bool CreatedBytecode = false; 207 std::string ErrMsg; 208 if (BytecodeFile.empty()) { 209 // Emit the program to a bytecode file... 210 sys::Path uniqueFilename("bugpoint-test-program.bc"); 211 if (uniqueFilename.makeUnique(true, &ErrMsg)) { 212 std::cerr << ToolName << ": Error making unique filename: " 213 << ErrMsg << "!\n"; 214 exit(1); 215 } 216 BytecodeFile = uniqueFilename.toString(); 217 218 if (writeProgramToFile(BytecodeFile, Program)) { 219 std::cerr << ToolName << ": Error emitting bytecode to file '" 220 << BytecodeFile << "'!\n"; 221 exit(1); 222 } 223 CreatedBytecode = true; 224 } 225 226 // Remove the temporary bytecode file when we are done. 227 sys::Path BytecodePath (BytecodeFile); 228 FileRemover BytecodeFileRemover(BytecodePath, CreatedBytecode); 229 230 if (OutputFile.empty()) OutputFile = "bugpoint-execution-output"; 231 232 // Check to see if this is a valid output filename... 233 sys::Path uniqueFile(OutputFile); 234 if (uniqueFile.makeUnique(true, &ErrMsg)) { 235 std::cerr << ToolName << ": Error making unique filename: " 236 << ErrMsg << "\n"; 237 exit(1); 238 } 239 OutputFile = uniqueFile.toString(); 240 241 // Figure out which shared objects to run, if any. 242 std::vector<std::string> SharedObjs(AdditionalSOs); 243 if (!SharedObj.empty()) 244 SharedObjs.push_back(SharedObj); 245 246 247 // If this is an LLC or CBE run, then the GCC compiler might get run to 248 // compile the program. If so, we should pass the user's -Xlinker options 249 // as the GCCArgs. 250 int RetVal = 0; 251 if (InterpreterSel == RunLLC || InterpreterSel == RunCBE || 252 InterpreterSel == CBE_bug) 253 RetVal = AI->ExecuteProgram(BytecodeFile, InputArgv, InputFile, 254 OutputFile, AdditionalLinkerArgs, SharedObjs, 255 Timeout); 256 else 257 RetVal = AI->ExecuteProgram(BytecodeFile, InputArgv, InputFile, 258 OutputFile, std::vector<std::string>(), 259 SharedObjs, Timeout); 260 261 if (RetVal == -1) { 262 std::cerr << "<timeout>"; 263 static bool FirstTimeout = true; 264 if (FirstTimeout) { 265 std::cout << "\n" 266 "*** Program execution timed out! This mechanism is designed to handle\n" 267 " programs stuck in infinite loops gracefully. The -timeout option\n" 268 " can be used to change the timeout threshold or disable it completely\n" 269 " (with -timeout=0). This message is only displayed once.\n"; 270 FirstTimeout = false; 271 } 272 } 273 274 if (ProgramExitedNonzero != 0) 275 *ProgramExitedNonzero = (RetVal != 0); 276 277 // Return the filename we captured the output to. 278 return OutputFile; 279} 280 281/// executeProgramWithCBE - Used to create reference output with the C 282/// backend, if reference output is not provided. 283/// 284std::string BugDriver::executeProgramWithCBE(std::string OutputFile) { 285 bool ProgramExitedNonzero; 286 std::string outFN = executeProgram(OutputFile, "", "", cbe, 287 &ProgramExitedNonzero); 288 if (ProgramExitedNonzero) { 289 std::cerr 290 << "Warning: While generating reference output, program exited with\n" 291 << "non-zero exit code. This will NOT be treated as a failure.\n"; 292 CheckProgramExitCode = false; 293 } 294 return outFN; 295} 296 297std::string BugDriver::compileSharedObject(const std::string &BytecodeFile) { 298 assert(Interpreter && "Interpreter should have been created already!"); 299 sys::Path OutputFile; 300 301 // Using CBE 302 GCC::FileType FT = cbe->OutputCode(BytecodeFile, OutputFile); 303 304 std::string SharedObjectFile; 305 if (gcc->MakeSharedObject(OutputFile.toString(), FT, 306 SharedObjectFile, AdditionalLinkerArgs)) 307 exit(1); 308 309 // Remove the intermediate C file 310 OutputFile.eraseFromDisk(); 311 312 return "./" + SharedObjectFile; 313} 314 315/// createReferenceFile - calls compileProgram and then records the output 316/// into ReferenceOutputFile. Returns true if reference file created, false 317/// otherwise. Note: initializeExecutionEnvironment should be called BEFORE 318/// this function. 319/// 320bool BugDriver::createReferenceFile(Module *M, const std::string &Filename) { 321 try { 322 compileProgram(Program); 323 } catch (ToolExecutionError &) { 324 return false; 325 } 326 try { 327 ReferenceOutputFile = executeProgramWithCBE(Filename); 328 std::cout << "Reference output is: " << ReferenceOutputFile << "\n\n"; 329 } catch (ToolExecutionError &TEE) { 330 std::cerr << TEE.what(); 331 if (Interpreter != cbe) { 332 std::cerr << "*** There is a bug running the C backend. Either debug" 333 << " it (use the -run-cbe bugpoint option), or fix the error" 334 << " some other way.\n"; 335 } 336 return false; 337 } 338 return true; 339} 340 341/// diffProgram - This method executes the specified module and diffs the 342/// output against the file specified by ReferenceOutputFile. If the output 343/// is different, true is returned. If there is a problem with the code 344/// generator (e.g., llc crashes), this will throw an exception. 345/// 346bool BugDriver::diffProgram(const std::string &BytecodeFile, 347 const std::string &SharedObject, 348 bool RemoveBytecode) { 349 bool ProgramExitedNonzero; 350 351 // Execute the program, generating an output file... 352 sys::Path Output(executeProgram("", BytecodeFile, SharedObject, 0, 353 &ProgramExitedNonzero)); 354 355 // If we're checking the program exit code, assume anything nonzero is bad. 356 if (CheckProgramExitCode && ProgramExitedNonzero) { 357 Output.eraseFromDisk(); 358 if (RemoveBytecode) 359 sys::Path(BytecodeFile).eraseFromDisk(); 360 return true; 361 } 362 363 std::string Error; 364 bool FilesDifferent = false; 365 if (int Diff = DiffFilesWithTolerance(sys::Path(ReferenceOutputFile), 366 sys::Path(Output.toString()), 367 AbsTolerance, RelTolerance, &Error)) { 368 if (Diff == 2) { 369 std::cerr << "While diffing output: " << Error << '\n'; 370 exit(1); 371 } 372 FilesDifferent = true; 373 } 374 375 // Remove the generated output. 376 Output.eraseFromDisk(); 377 378 // Remove the bytecode file if we are supposed to. 379 if (RemoveBytecode) 380 sys::Path(BytecodeFile).eraseFromDisk(); 381 return FilesDifferent; 382} 383 384bool BugDriver::isExecutingJIT() { 385 return InterpreterSel == RunJIT; 386} 387 388