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