| //===-- 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. | |
| 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; | |
| } | |
| } |