ExecutionDriver.cpp revision 7d91e49ff7bcc0fd10a54d45a6185bb05adf3d20
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 "Support/CommandLine.h"
18#include "Support/Debug.h"
19#include "Support/FileUtilities.h"
20#include "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  std::string BytecodeFile = getUniqueFilename("bugpoint-test-program.bc");
156  if (writeProgramToFile(BytecodeFile, M)) {
157    std::cerr << ToolName << ": Error emitting bytecode to file '"
158              << BytecodeFile << "'!\n";
159    exit(1);
160  }
161
162    // Remove the temporary bytecode file when we are done.
163  FileRemover BytecodeFileRemover(BytecodeFile);
164
165  // Actually compile the program!
166  Interpreter->compileProgram(BytecodeFile);
167}
168
169
170/// executeProgram - This method runs "Program", capturing the output of the
171/// program to a file, returning the filename of the file.  A recommended
172/// filename may be optionally specified.
173///
174std::string BugDriver::executeProgram(std::string OutputFile,
175                                      std::string BytecodeFile,
176                                      const std::string &SharedObj,
177                                      AbstractInterpreter *AI,
178                                      bool *ProgramExitedNonzero) {
179  if (AI == 0) AI = Interpreter;
180  assert(AI && "Interpreter should have been created already!");
181  bool CreatedBytecode = false;
182  if (BytecodeFile.empty()) {
183    // Emit the program to a bytecode file...
184    BytecodeFile = getUniqueFilename("bugpoint-test-program.bc");
185
186    if (writeProgramToFile(BytecodeFile, Program)) {
187      std::cerr << ToolName << ": Error emitting bytecode to file '"
188                << BytecodeFile << "'!\n";
189      exit(1);
190    }
191    CreatedBytecode = true;
192  }
193
194  // Remove the temporary bytecode file when we are done.
195  FileRemover BytecodeFileRemover(BytecodeFile, CreatedBytecode);
196
197  if (OutputFile.empty()) OutputFile = "bugpoint-execution-output";
198
199  // Check to see if this is a valid output filename...
200  OutputFile = getUniqueFilename(OutputFile);
201
202  // Figure out which shared objects to run, if any.
203  std::vector<std::string> SharedObjs(AdditionalSOs);
204  if (!SharedObj.empty())
205    SharedObjs.push_back(SharedObj);
206
207  // Actually execute the program!
208  int RetVal = AI->ExecuteProgram(BytecodeFile, InputArgv, InputFile,
209                                  OutputFile, SharedObjs, TimeoutValue);
210
211  if (RetVal == -1) {
212    std::cerr << "<timeout>";
213    static bool FirstTimeout = true;
214    if (FirstTimeout) {
215      std::cout << "\n"
216 "*** Program execution timed out!  This mechanism is designed to handle\n"
217 "    programs stuck in infinite loops gracefully.  The -timeout option\n"
218 "    can be used to change the timeout threshold or disable it completely\n"
219 "    (with -timeout=0).  This message is only displayed once.\n";
220      FirstTimeout = false;
221    }
222  }
223
224  if (ProgramExitedNonzero != 0)
225    *ProgramExitedNonzero = (RetVal != 0);
226
227  // Return the filename we captured the output to.
228  return OutputFile;
229}
230
231/// executeProgramWithCBE - Used to create reference output with the C
232/// backend, if reference output is not provided.
233///
234std::string BugDriver::executeProgramWithCBE(std::string OutputFile) {
235  bool ProgramExitedNonzero;
236  std::string outFN = executeProgram(OutputFile, "", "",
237                                     (AbstractInterpreter*)cbe,
238                                     &ProgramExitedNonzero);
239  if (ProgramExitedNonzero) {
240    std::cerr
241      << "Warning: While generating reference output, program exited with\n"
242      << "non-zero exit code. This will NOT be treated as a failure.\n";
243    CheckProgramExitCode = false;
244  }
245  return outFN;
246}
247
248std::string BugDriver::compileSharedObject(const std::string &BytecodeFile) {
249  assert(Interpreter && "Interpreter should have been created already!");
250  std::string OutputCFile;
251
252  // Using CBE
253  cbe->OutputC(BytecodeFile, OutputCFile);
254
255#if 0 /* This is an alternative, as yet unimplemented */
256  // Using LLC
257  std::string Message;
258  LLC *llc = createLLCtool(Message);
259  if (llc->OutputAsm(BytecodeFile, OutputFile)) {
260    std::cerr << "Could not generate asm code with `llc', exiting.\n";
261    exit(1);
262  }
263#endif
264
265  std::string SharedObjectFile;
266  if (gcc->MakeSharedObject(OutputCFile, GCC::CFile, SharedObjectFile))
267    exit(1);
268
269  // Remove the intermediate C file
270  removeFile(OutputCFile);
271
272  return "./" + SharedObjectFile;
273}
274
275
276/// diffProgram - This method executes the specified module and diffs the output
277/// against the file specified by ReferenceOutputFile.  If the output is
278/// different, true is returned.
279///
280bool BugDriver::diffProgram(const std::string &BytecodeFile,
281                            const std::string &SharedObject,
282                            bool RemoveBytecode) {
283  bool ProgramExitedNonzero;
284
285  // Execute the program, generating an output file...
286  std::string Output = executeProgram("", BytecodeFile, SharedObject, 0,
287                                      &ProgramExitedNonzero);
288
289  // If we're checking the program exit code, assume anything nonzero is bad.
290  if (CheckProgramExitCode && ProgramExitedNonzero) {
291    removeFile(Output);
292    if (RemoveBytecode) removeFile(BytecodeFile);
293    return true;
294  }
295
296  std::string Error;
297  bool FilesDifferent = false;
298  if (DiffFiles(ReferenceOutputFile, Output, &Error)) {
299    if (!Error.empty()) {
300      std::cerr << "While diffing output: " << Error << '\n';
301      exit(1);
302    }
303    FilesDifferent = true;
304  }
305
306  // Remove the generated output.
307  removeFile(Output);
308
309  // Remove the bytecode file if we are supposed to.
310  if (RemoveBytecode) removeFile(BytecodeFile);
311  return FilesDifferent;
312}
313
314bool BugDriver::isExecutingJIT() {
315  return InterpreterSel == RunJIT;
316}
317
318