Initial checkin of the LLVM source-level debugger.  This is still not finished,
by any stretch of the imagination, but it is pretty cool and works :)


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@10685 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Debugger/Debugger.cpp b/lib/Debugger/Debugger.cpp
new file mode 100644
index 0000000..fd11f23
--- /dev/null
+++ b/lib/Debugger/Debugger.cpp
@@ -0,0 +1,212 @@
+//===-- Debugger.cpp - LLVM debugger library implementation ---------------===//
+// 
+//                     The LLVM Compiler Infrastructure
+//
+// This file was developed by the LLVM research group and is distributed under
+// the University of Illinois Open Source License. See LICENSE.TXT for details.
+// 
+//===----------------------------------------------------------------------===//
+// 
+// This file contains the main implementation of the LLVM debugger library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Debugger/Debugger.h"
+#include "llvm/Module.h"
+#include "llvm/ModuleProvider.h"
+#include "llvm/Bytecode/Reader.h"
+#include "llvm/Debugger/InferiorProcess.h"
+#include "Support/StringExtras.h"
+using namespace llvm;
+
+/// Debugger constructor - Initialize the debugger to its initial, empty, state.
+///
+Debugger::Debugger() : Environment(0), Program(0), Process(0) {
+}
+
+Debugger::~Debugger() {
+  // Killing the program could throw an exception.  We don't want to progagate
+  // the exception out of our destructor though.
+  try {
+    killProgram();
+  } catch (const char *) {
+  } catch (const std::string &) {
+  }
+  
+  unloadProgram();
+}
+
+/// getProgramPath - Get the path of the currently loaded program, or an
+/// empty string if none is loaded.
+std::string Debugger::getProgramPath() const {
+  return Program ? Program->getModuleIdentifier() : "";
+}
+
+static Module *
+getMaterializedModuleProvider(const std::string &Filename) {
+  try {
+    std::auto_ptr<ModuleProvider> Result(getBytecodeModuleProvider(Filename));
+    if (!Result.get()) return 0;
+  
+    Result->materializeModule();
+    return Result.release()->releaseModule();
+  } catch (...) {
+    return 0;
+  }
+}
+
+/// loadProgram - If a program is currently loaded, unload it.  Then search
+/// the PATH for the specified program, loading it when found.  If the
+/// specified program cannot be found, an exception is thrown to indicate the
+/// error.
+void Debugger::loadProgram(const std::string &Filename) {
+  if ((Program = getMaterializedModuleProvider(Filename)) ||
+      (Program = getMaterializedModuleProvider(Filename+".bc")))
+    return;   // Successfully loaded the program.
+
+  // Search the program path for the file...
+  if (const char *PathS = getenv("PATH")) {
+    std::string Path = PathS;
+
+    std::string Directory = getToken(Path, ":");
+    while (!Directory.empty()) {
+      if ((Program = getMaterializedModuleProvider(Directory +"/"+ Filename)) ||
+          (Program = getMaterializedModuleProvider(Directory +"/"+ Filename
+                                                                      + ".bc")))
+        return;   // Successfully loaded the program.
+
+      Directory = getToken(Path, ":");
+    }
+  }
+
+  throw "Could not find program '" + Filename + "'!";
+}
+
+/// unloadProgram - If a program is running, kill it, then unload all traces
+/// of the current program.  If no program is loaded, this method silently
+/// succeeds.
+void Debugger::unloadProgram() {
+  if (!isProgramLoaded()) return;
+  killProgram();
+  delete Program;
+  Program = 0;
+}
+
+
+/// createProgram - Create an instance of the currently loaded program,
+/// killing off any existing one.  This creates the program and stops it at
+/// the first possible moment.  If there is no program loaded or if there is a
+/// problem starting the program, this method throws an exception.
+void Debugger::createProgram() {
+  if (!isProgramLoaded())
+    throw "Cannot start program: none is loaded.";
+
+  // Kill any existing program.
+  killProgram();
+
+  // Add argv[0] to the arguments vector..
+  std::vector<std::string> Args(ProgramArguments);
+  Args.insert(Args.begin(), getProgramPath());
+
+  // Start the new program... this could throw if the program cannot be started.
+  Process = InferiorProcess::create(Program, Args, Environment);
+}
+
+/// killProgram - If the program is currently executing, kill off the
+/// process and free up any state related to the currently running program.  If
+/// there is no program currently running, this just silently succeeds.
+void Debugger::killProgram() {
+  // The destructor takes care of the dirty work.
+  delete Process;
+  Process = 0;
+}
+
+/// stepProgram - Implement the 'step' command, continuing execution until
+/// the next possible stop point.
+void Debugger::stepProgram() {
+  assert(isProgramRunning() && "Cannot step if the program isn't running!");
+  try {
+    Process->stepProgram();
+  } catch (InferiorProcessDead &IPD) {
+    delete Process;
+    Process = 0;
+    throw NonErrorException("The program stopped with exit code " +
+                            itostr(IPD.getExitCode()));
+  }
+}
+
+/// nextProgram - Implement the 'next' command, continuing execution until
+/// the next possible stop point that is in the current function.
+void Debugger::nextProgram() {
+  assert(isProgramRunning() && "Cannot next if the program isn't running!");
+  try {
+    // This should step the process.  If the process enters a function, then it
+    // should 'finish' it.  However, figuring this out is tricky.  In
+    // particular, the program can do any of:
+    //  0. Not change current frame.
+    //  1. Entering or exiting a region within the current function
+    //     (which changes the frame ID, but which we shouldn't 'finish')
+    //  2. Exiting the current function (which changes the frame ID)
+    //  3. Entering a function (which should be 'finish'ed)
+    // For this reason, we have to be very careful about when we decide to do
+    // the 'finish'.
+
+    // Get the current frame, but don't trust it.  It could change...
+    void *CurrentFrame = Process->getPreviousFrame(0);
+
+    // Don't trust the current frame: get the caller frame.
+    void *ParentFrame  = Process->getPreviousFrame(CurrentFrame);
+    
+    // Ok, we have some information, run the program one step.
+    Process->stepProgram();
+
+    // Where is the new frame?  The most common case, by far is that it has not
+    // been modified (Case #0), in which case we don't need to do anything more.
+    void *NewFrame = Process->getPreviousFrame(0);
+    if (NewFrame != CurrentFrame) {
+      // Ok, the frame changed.  If we are case #1, then the parent frame will
+      // be identical.
+      void *NewParentFrame = Process->getPreviousFrame(NewFrame);
+      if (ParentFrame != NewParentFrame) {
+        // Ok, now we know we aren't case #0 or #1.  Check to see if we entered
+        // a new function.  If so, the parent frame will be "CurrentFrame".
+        if (CurrentFrame == NewParentFrame)
+          Process->finishProgram(NewFrame);
+      }
+    }
+
+  } catch (InferiorProcessDead &IPD) {
+    delete Process;
+    Process = 0;
+    throw NonErrorException("The program stopped with exit code " +
+                            itostr(IPD.getExitCode()));
+  }
+}
+
+/// finishProgram - Implement the 'finish' command, continuing execution
+/// until the specified frame ID returns.
+void Debugger::finishProgram(void *Frame) {
+  assert(isProgramRunning() && "Cannot cont if the program isn't running!");
+  try {
+    Process->finishProgram(Frame);
+  } catch (InferiorProcessDead &IPD) {
+    delete Process;
+    Process = 0;
+    throw NonErrorException("The program stopped with exit code " +
+                            itostr(IPD.getExitCode()));
+  }
+}
+
+/// contProgram - Implement the 'cont' command, continuing execution until
+/// the next breakpoint is encountered.
+void Debugger::contProgram() {
+  assert(isProgramRunning() && "Cannot cont if the program isn't running!");
+  try {
+    Process->contProgram();
+  } catch (InferiorProcessDead &IPD) {
+    delete Process;
+    Process = 0;
+    throw NonErrorException("The program stopped with exit code " +
+                            itostr(IPD.getExitCode()));
+  }
+}