226 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- 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 "llvm/ADT/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.
 | |
|   try {
 | |
|     delete Process;
 | |
|   } catch (...) {
 | |
|     Process = 0;
 | |
|     throw;
 | |
|   }
 | |
|   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) {
 | |
|     killProgram();
 | |
|     throw NonErrorException("The program stopped with exit code " +
 | |
|                             itostr(IPD.getExitCode()));
 | |
|   } catch (...) {
 | |
|     killProgram();
 | |
|     throw;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// 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) {
 | |
|     killProgram();
 | |
|     throw NonErrorException("The program stopped with exit code " +
 | |
|                             itostr(IPD.getExitCode()));
 | |
|   } catch (...) {
 | |
|     killProgram();
 | |
|     throw;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// 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) {
 | |
|     killProgram();
 | |
|     throw NonErrorException("The program stopped with exit code " +
 | |
|                             itostr(IPD.getExitCode()));
 | |
|   } catch (...) {
 | |
|     killProgram();
 | |
|     throw;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// 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) {
 | |
|     killProgram();
 | |
|     throw NonErrorException("The program stopped with exit code " +
 | |
|                             itostr(IPD.getExitCode()));
 | |
|   } catch (...) {
 | |
|     killProgram();
 | |
|     throw;
 | |
|   }
 | |
| }
 |