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