diff options
author | Chris Lattner <sabre@nondot.org> | 2006-03-10 22:39:48 +0000 |
---|---|---|
committer | Chris Lattner <sabre@nondot.org> | 2006-03-10 22:39:48 +0000 |
commit | 89b192690ad3c513cfc8f093ca6dcbd59800a971 (patch) | |
tree | fc0655d1dab647b5abe79265c6be4842e1f77a8d /lib/Debugger | |
parent | 502fe92a688859370e30d04e59c9707a24d829b5 (diff) | |
download | external_llvm-89b192690ad3c513cfc8f093ca6dcbd59800a971.zip external_llvm-89b192690ad3c513cfc8f093ca6dcbd59800a971.tar.gz external_llvm-89b192690ad3c513cfc8f093ca6dcbd59800a971.tar.bz2 |
Remove UnixLocalInferiorProcess: debugging via the JIT isn't a good idea.
Perhaps llvm-db will turn into a read debugger someday.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@26700 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Debugger')
-rw-r--r-- | lib/Debugger/Debugger.cpp | 6 | ||||
-rw-r--r-- | lib/Debugger/UnixLocalInferiorProcess.cpp | 937 |
2 files changed, 6 insertions, 937 deletions
diff --git a/lib/Debugger/Debugger.cpp b/lib/Debugger/Debugger.cpp index f7108e4..a0505bd 100644 --- a/lib/Debugger/Debugger.cpp +++ b/lib/Debugger/Debugger.cpp @@ -113,6 +113,12 @@ void Debugger::createProgram() { Process = InferiorProcess::create(Program, Args, Environment); } +InferiorProcess * +InferiorProcess::create(Module *M, const std::vector<std::string> &Arguments, + const char * const *envp) { + throw"No supported binding to inferior processes (debugger not implemented)."; +} + /// 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. diff --git a/lib/Debugger/UnixLocalInferiorProcess.cpp b/lib/Debugger/UnixLocalInferiorProcess.cpp deleted file mode 100644 index ef9a0a9..0000000 --- a/lib/Debugger/UnixLocalInferiorProcess.cpp +++ /dev/null @@ -1,937 +0,0 @@ -//===-- UnixLocalInferiorProcess.cpp - A Local process on a Unixy system --===// -// -// 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 provides one implementation of the InferiorProcess class, which is -// designed to be used on unixy systems (those that support pipe, fork, exec, -// and signals). -// -// When the process is started, the debugger creates a pair of pipes, forks, and -// makes the child start executing the program. The child executes the process -// with an IntrinsicLowering instance that turns debugger intrinsics into actual -// callbacks. -// -// This target takes advantage of the fact that the Module* addresses in the -// parent and the Module* addresses in the child will be the same, due to the -// use of fork(). As such, global addresses looked up in the child can be sent -// over the pipe to the debugger. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Debugger/InferiorProcess.h" -#include "llvm/Constant.h" -#include "llvm/Instructions.h" -#include "llvm/Module.h" -#include "llvm/ModuleProvider.h" -#include "llvm/Type.h" -#include "llvm/CodeGen/IntrinsicLowering.h" -#include "llvm/ExecutionEngine/GenericValue.h" -#include "llvm/ExecutionEngine/ExecutionEngine.h" -#include "llvm/Support/FileUtilities.h" -#include "llvm/ADT/StringExtras.h" -#include "FDHandle.h" -#include <cerrno> -#include <csignal> -#include <unistd.h> // Unix-specific debugger support -#include <sys/types.h> -#include <sys/wait.h> -using namespace llvm; - -// runChild - Entry point for the child process. -static void runChild(Module *M, const std::vector<std::string> &Arguments, - const char * const *envp, - FDHandle ReadFD, FDHandle WriteFD); - -//===----------------------------------------------------------------------===// -// Parent/Child Pipe Protocol -//===----------------------------------------------------------------------===// -// -// The parent/child communication protocol is designed to have the child process -// responding to requests that the debugger makes. Whenever the child process -// has stopped (due to a break point, single stepping, etc), the child process -// enters a message processing loop, where it reads and responds to commands -// until the parent decides that it wants to continue execution in some way. -// -// Whenever the child process stops, it notifies the debugger by sending a -// character over the wire. -// - -namespace { - /// LocationToken - Objects of this type are sent across the pipe from the - /// child to the parent to indicate where various stack frames are located. - struct LocationToken { - unsigned Line, Col; - const GlobalVariable *File; - LocationToken(unsigned L = 0, unsigned C = 0, const GlobalVariable *F = 0) - : Line(L), Col(C), File(F) {} - }; -} - -// Once the debugger process has received the LocationToken, it can make -// requests of the child by sending one of the following enum values followed by -// any data required by that command. The child responds with data appropriate -// to the command. -// -namespace { - /// CommandID - This enum defines all of the commands that the child process - /// can respond to. The actual expected data and responses are defined as the - /// enum values are defined. - /// - enum CommandID { - //===------------------------------------------------------------------===// - // Execution commands - These are sent to the child to from the debugger to - // get it to do certain things. - // - - // StepProgram: void->char - This command causes the program to continue - // execution, but stop as soon as it reaches another stoppoint. - StepProgram, - - // FinishProgram: FrameDesc*->char - This command causes the program to - // continue execution until the specified function frame returns. - FinishProgram, - - // ContProgram: void->char - This command causes the program to continue - // execution, stopping at some point in the future. - ContProgram, - - // GetSubprogramDescriptor: FrameDesc*->GlobalValue* - This command returns - // the GlobalValue* descriptor object for the specified stack frame. - GetSubprogramDescriptor, - - // GetParentFrame: FrameDesc*->FrameDesc* - This command returns the frame - // descriptor for the parent stack frame to the specified one, or null if - // there is none. - GetParentFrame, - - // GetFrameLocation - FrameDesc*->LocationToken - This command returns the - // location that a particular stack frame is stopped at. - GetFrameLocation, - - // AddBreakpoint - LocationToken->unsigned - This command instructs the - // target to install a breakpoint at the specified location. - AddBreakpoint, - - // RemoveBreakpoint - unsigned->void - This command instructs the target to - // remove a breakpoint. - RemoveBreakpoint, - }; -} - - - - -//===----------------------------------------------------------------------===// -// Parent Process Code -//===----------------------------------------------------------------------===// - -namespace { - class IP : public InferiorProcess { - // ReadFD, WriteFD - The file descriptors to read/write to the inferior - // process. - FDHandle ReadFD, WriteFD; - - // ChildPID - The unix PID of the child process we forked. - mutable pid_t ChildPID; - public: - IP(Module *M, const std::vector<std::string> &Arguments, - const char * const *envp); - ~IP(); - - std::string getStatus() const; - - /// Execution method implementations... - virtual void stepProgram(); - virtual void finishProgram(void *Frame); - virtual void contProgram(); - - - // Stack frame method implementations... - virtual void *getPreviousFrame(void *Frame) const; - virtual const GlobalVariable *getSubprogramDesc(void *Frame) const; - virtual void getFrameLocation(void *Frame, unsigned &LineNo, - unsigned &ColNo, - const GlobalVariable *&SourceDesc) const; - - // Breakpoint implementation methods - virtual unsigned addBreakpoint(unsigned LineNo, unsigned ColNo, - const GlobalVariable *SourceDesc); - virtual void removeBreakpoint(unsigned ID); - - - private: - /// startChild - This starts up the child process, and initializes the - /// ChildPID member. - /// - void startChild(Module *M, const std::vector<std::string> &Arguments, - const char * const *envp); - - /// killChild - Kill or reap the child process. This throws the - /// InferiorProcessDead exception an exit code if the process had already - /// died, otherwise it just kills it and returns. - void killChild() const; - - private: - // Methods for communicating with the child process. If the child exits or - // dies while attempting to communicate with it, ChildPID is set to zero and - // an exception is thrown. - - /// readFromChild - Low-level primitive to read some data from the child, - /// throwing an exception if it dies. - void readFromChild(void *Buffer, unsigned Size) const; - - /// writeToChild - Low-level primitive to send some data to the child - /// process, throwing an exception if the child died. - void writeToChild(void *Buffer, unsigned Size) const; - - /// sendCommand - Send a command token and the request data to the child. - /// - void sendCommand(CommandID Command, void *Data, unsigned Size) const; - - /// waitForStop - This method waits for the child process to reach a stop - /// point. - void waitForStop(); - }; -} - -// create - This is the factory method for the InferiorProcess class. Since -// there is currently only one subclass of InferiorProcess, we just define it -// here. -InferiorProcess * -InferiorProcess::create(Module *M, const std::vector<std::string> &Arguments, - const char * const *envp) { - return new IP(M, Arguments, envp); -} - -/// IP constructor - Create some pipes, them fork a child process. The child -/// process should start execution of the debugged program, but stop at the -/// first available opportunity. -IP::IP(Module *M, const std::vector<std::string> &Arguments, - const char * const *envp) - : InferiorProcess(M) { - - // Start the child running... - startChild(M, Arguments, envp); - - // Okay, we created the program and it is off and running. Wait for it to - // stop now. - try { - waitForStop(); - } catch (InferiorProcessDead &IPD) { - throw "Error waiting for the child process to stop. " - "It exited with status " + itostr(IPD.getExitCode()); - } -} - -IP::~IP() { - // If the child is still running, kill it. - if (!ChildPID) return; - - killChild(); -} - -/// getStatus - Return information about the unix process being debugged. -/// -std::string IP::getStatus() const { - if (ChildPID == 0) - return "Unix target. ERROR: child process appears to be dead!\n"; - - return "Unix target: PID #" + utostr((unsigned)ChildPID) + "\n"; -} - - -/// startChild - This starts up the child process, and initializes the -/// ChildPID member. -/// -void IP::startChild(Module *M, const std::vector<std::string> &Arguments, - const char * const *envp) { - // Create the pipes. Make sure to immediately assign the returned file - // descriptors to FDHandle's so they get destroyed if an exception is thrown. - int FDs[2]; - if (pipe(FDs)) throw "Error creating a pipe!"; - FDHandle ChildReadFD(FDs[0]); - WriteFD = FDs[1]; - - if (pipe(FDs)) throw "Error creating a pipe!"; - ReadFD = FDs[0]; - FDHandle ChildWriteFD(FDs[1]); - - // Fork off the child process. - switch (ChildPID = fork()) { - case -1: throw "Error forking child process!"; - case 0: // child - delete this; // Free parent pipe file descriptors - runChild(M, Arguments, envp, ChildReadFD, ChildWriteFD); - exit(1); - default: break; - } -} - -/// sendCommand - Send a command token and the request data to the child. -/// -void IP::sendCommand(CommandID Command, void *Data, unsigned Size) const { - writeToChild(&Command, sizeof(Command)); - writeToChild(Data, Size); -} - -/// stepProgram - Implement the 'step' command, continuing execution until -/// the next possible stop point. -void IP::stepProgram() { - sendCommand(StepProgram, 0, 0); - waitForStop(); -} - -/// finishProgram - Implement the 'finish' command, executing the program until -/// the current function returns to its caller. -void IP::finishProgram(void *Frame) { - sendCommand(FinishProgram, &Frame, sizeof(Frame)); - waitForStop(); -} - -/// contProgram - Implement the 'cont' command, continuing execution until -/// a breakpoint is encountered. -void IP::contProgram() { - sendCommand(ContProgram, 0, 0); - waitForStop(); -} - - -//===----------------------------------------------------------------------===// -// Stack manipulation methods -// - -/// getPreviousFrame - Given the descriptor for the current stack frame, -/// return the descriptor for the caller frame. This returns null when it -/// runs out of frames. -void *IP::getPreviousFrame(void *Frame) const { - sendCommand(GetParentFrame, &Frame, sizeof(Frame)); - readFromChild(&Frame, sizeof(Frame)); - return Frame; -} - -/// getSubprogramDesc - Return the subprogram descriptor for the current -/// stack frame. -const GlobalVariable *IP::getSubprogramDesc(void *Frame) const { - sendCommand(GetSubprogramDescriptor, &Frame, sizeof(Frame)); - const GlobalVariable *Desc; - readFromChild(&Desc, sizeof(Desc)); - return Desc; -} - -/// getFrameLocation - This method returns the source location where each stack -/// frame is stopped. -void IP::getFrameLocation(void *Frame, unsigned &LineNo, unsigned &ColNo, - const GlobalVariable *&SourceDesc) const { - sendCommand(GetFrameLocation, &Frame, sizeof(Frame)); - LocationToken Loc; - readFromChild(&Loc, sizeof(Loc)); - LineNo = Loc.Line; - ColNo = Loc.Col; - SourceDesc = Loc.File; -} - - -//===----------------------------------------------------------------------===// -// Breakpoint manipulation methods -// -unsigned IP::addBreakpoint(unsigned LineNo, unsigned ColNo, - const GlobalVariable *SourceDesc) { - LocationToken Loc; - Loc.Line = LineNo; - Loc.Col = ColNo; - Loc.File = SourceDesc; - sendCommand(AddBreakpoint, &Loc, sizeof(Loc)); - unsigned ID; - readFromChild(&ID, sizeof(ID)); - return ID; -} - -void IP::removeBreakpoint(unsigned ID) { - sendCommand(RemoveBreakpoint, &ID, sizeof(ID)); -} - - -//===----------------------------------------------------------------------===// -// Methods for communication with the child process -// -// Methods for communicating with the child process. If the child exits or dies -// while attempting to communicate with it, ChildPID is set to zero and an -// exception is thrown. -// - -/// readFromChild - Low-level primitive to read some data from the child, -/// throwing an exception if it dies. -void IP::readFromChild(void *Buffer, unsigned Size) const { - assert(ChildPID && - "Child process died and still attempting to communicate with it!"); - while (Size) { - ssize_t Amount = read(ReadFD, Buffer, Size); - if (Amount == 0) { - // If we cannot communicate with the process, kill it. - killChild(); - // If killChild succeeded, then the process must have closed the pipe FD - // or something, because the child existed, but we cannot communicate with - // it. - throw InferiorProcessDead(-1); - } else if (Amount == -1) { - if (errno != EINTR) { - ChildPID = 0; - killChild(); - throw "Error reading from child process!"; - } - } else { - // We read a chunk. - Buffer = (char*)Buffer + Amount; - Size -= Amount; - } - } -} - -/// writeToChild - Low-level primitive to send some data to the child -/// process, throwing an exception if the child died. -void IP::writeToChild(void *Buffer, unsigned Size) const { - while (Size) { - ssize_t Amount = write(WriteFD, Buffer, Size); - if (Amount < 0 && errno == EINTR) continue; - if (Amount <= 0) { - // If we cannot communicate with the process, kill it. - killChild(); - - // If killChild succeeded, then the process must have closed the pipe FD - // or something, because the child existed, but we cannot communicate with - // it. - throw InferiorProcessDead(-1); - } else { - // We wrote a chunk. - Buffer = (char*)Buffer + Amount; - Size -= Amount; - } - } -} - -/// killChild - Kill or reap the child process. This throws the -/// InferiorProcessDead exception an exit code if the process had already -/// died, otherwise it just returns the exit code if it had to be killed. -void IP::killChild() const { - assert(ChildPID != 0 && "Child has already been reaped!"); - - // If the process terminated on its own accord, closing the pipe file - // descriptors, we will get here. Check to see if the process has already - // died in this manner, gracefully. - int Status = 0; - int PID; - do { - PID = waitpid(ChildPID, &Status, WNOHANG); - } while (PID < 0 && errno == EINTR); - if (PID < 0) throw "Error waiting for child to exit!"; - - // Ok, there is a slight race condition here. It's possible that we will find - // out that the file descriptor closed before waitpid will indicate that the - // process gracefully died. If we don't know that the process gracefully - // died, wait a bit and try again. This is pretty nasty. - if (PID == 0) { - usleep(10000); // Wait a bit. - - // Try again. - Status = 0; - do { - PID = waitpid(ChildPID, &Status, WNOHANG); - } while (PID < 0 && errno == EINTR); - if (PID < 0) throw "Error waiting for child to exit!"; - } - - // If the child process was already dead, then indicate that the process - // terminated on its own. - if (PID) { - assert(PID == ChildPID && "Didn't reap child?"); - ChildPID = 0; // Child has been reaped - if (WIFEXITED(Status)) - throw InferiorProcessDead(WEXITSTATUS(Status)); - else if (WIFSIGNALED(Status)) - throw InferiorProcessDead(WTERMSIG(Status)); - throw InferiorProcessDead(-1); - } - - // Otherwise, the child exists and has not yet been killed. - if (kill(ChildPID, SIGKILL) < 0) - throw "Error killing child process!"; - - do { - PID = waitpid(ChildPID, 0, 0); - } while (PID < 0 && errno == EINTR); - if (PID <= 0) throw "Error waiting for child to exit!"; - - assert(PID == ChildPID && "Didn't reap child?"); -} - - -/// waitForStop - This method waits for the child process to reach a stop -/// point. When it does, it fills in the CurLocation member and returns. -void IP::waitForStop() { - char Dummy; - readFromChild(&Dummy, sizeof(char)); -} - - -//===----------------------------------------------------------------------===// -// Child Process Code -//===----------------------------------------------------------------------===// - -namespace { - class SourceSubprogram; - - /// SourceRegion - Instances of this class represent the regions that are - /// active in the program. - class SourceRegion { - /// Parent - A pointer to the region that encloses the current one. - SourceRegion *Parent; - - /// CurSubprogram - The subprogram that contains this region. This allows - /// efficient stack traversals. - SourceSubprogram *CurSubprogram; - - /// CurLine, CurCol, CurFile - The last location visited by this region. - /// This is used for getting the source location of callers in stack frames. - unsigned CurLine, CurCol; - void *CurFileDesc; - - //std::vector<void*> ActiveObjects; - public: - SourceRegion(SourceRegion *p, SourceSubprogram *Subprogram = 0) - : Parent(p), CurSubprogram(Subprogram ? Subprogram : p->getSubprogram()) { - CurLine = 0; CurCol = 0; - CurFileDesc = 0; - } - - virtual ~SourceRegion() {} - - SourceRegion *getParent() const { return Parent; } - SourceSubprogram *getSubprogram() const { return CurSubprogram; } - - void updateLocation(unsigned Line, unsigned Col, void *File) { - CurLine = Line; - CurCol = Col; - CurFileDesc = File; - } - - /// Return a LocationToken for the place that this stack frame stopped or - /// called a sub-function. - LocationToken getLocation(ExecutionEngine *EE) { - LocationToken LT; - LT.Line = CurLine; - LT.Col = CurCol; - const GlobalValue *GV = EE->getGlobalValueAtAddress(CurFileDesc); - LT.File = dyn_cast_or_null<GlobalVariable>(GV); - return LT; - } - }; - - /// SourceSubprogram - This is a stack-frame that represents a source program. - /// - class SourceSubprogram : public SourceRegion { - /// Desc - A pointer to the descriptor for the subprogram that this frame - /// represents. - void *Desc; - public: - SourceSubprogram(SourceRegion *P, void *desc) - : SourceRegion(P, this), Desc(desc) {} - void *getDescriptor() const { return Desc; } - }; - - - /// Child class - This class contains all of the information and methods used - /// by the child side of the debugger. The single instance of this object is - /// pointed to by the "TheChild" global variable. - class Child { - /// M - The module for the program currently being debugged. - /// - Module *M; - - /// EE - The execution engine that we are using to run the program. - /// - ExecutionEngine *EE; - - /// ReadFD, WriteFD - The file descriptor handles for this side of the - /// debugger pipe. - FDHandle ReadFD, WriteFD; - - /// RegionStack - A linked list of all of the regions dynamically active. - /// - SourceRegion *RegionStack; - - /// StopAtNextOpportunity - If this flag is set, the child process will stop - /// and report to the debugger at the next possible chance it gets. - volatile bool StopAtNextOpportunity; - - /// StopWhenSubprogramReturns - If this is non-null, the debugger requests - /// that the program stops when the specified function frame is destroyed. - SourceSubprogram *StopWhenSubprogramReturns; - - /// Breakpoints - This contains a list of active breakpoints and their IDs. - /// - std::vector<std::pair<unsigned, LocationToken> > Breakpoints; - - /// CurBreakpoint - The last assigned breakpoint. - /// - unsigned CurBreakpoint; - - public: - Child(Module *m, ExecutionEngine *ee, FDHandle &Read, FDHandle &Write) - : M(m), EE(ee), ReadFD(Read), WriteFD(Write), - RegionStack(0), CurBreakpoint(0) { - StopAtNextOpportunity = true; - StopWhenSubprogramReturns = 0; - } - - /// writeToParent - Send the specified buffer of data to the debugger - /// process. - /// - void writeToParent(const void *Buffer, unsigned Size); - - /// readFromParent - Read the specified number of bytes from the parent. - /// - void readFromParent(void *Buffer, unsigned Size); - - /// childStopped - This method is called whenever the child has stopped - /// execution due to a breakpoint, step command, interruption, or whatever. - /// This stops the process, responds to any requests from the debugger, and - /// when commanded to, can continue execution by returning. - /// - void childStopped(); - - /// startSubprogram - This method creates a new region for the subroutine - /// with the specified descriptor. - /// - void startSubprogram(void *FuncDesc); - - /// startRegion - This method initiates the creation of an anonymous region. - /// - void startRegion(); - - /// endRegion - This method terminates the last active region. - /// - void endRegion(); - - /// reachedLine - This method is automatically called by the program every - /// time it executes an llvm.dbg.stoppoint intrinsic. If the debugger wants - /// us to stop here, we do so, otherwise we continue execution. - /// - void reachedLine(unsigned Line, unsigned Col, void *SourceDesc); - }; - - /// TheChild - The single instance of the Child class, which only gets created - /// in the child process. - Child *TheChild = 0; -} // end anonymous namespace - - -// writeToParent - Send the specified buffer of data to the debugger process. -void Child::writeToParent(const void *Buffer, unsigned Size) { - while (Size) { - ssize_t Amount = write(WriteFD, Buffer, Size); - if (Amount < 0 && errno == EINTR) continue; - if (Amount <= 0) { - write(2, "ERROR: Connection to debugger lost!\n", 36); - abort(); - } else { - // We wrote a chunk. - Buffer = (const char*)Buffer + Amount; - Size -= Amount; - } - } -} - -// readFromParent - Read the specified number of bytes from the parent. -void Child::readFromParent(void *Buffer, unsigned Size) { - while (Size) { - ssize_t Amount = read(ReadFD, Buffer, Size); - if (Amount < 0 && errno == EINTR) continue; - if (Amount <= 0) { - write(2, "ERROR: Connection to debugger lost!\n", 36); - abort(); - } else { - // We read a chunk. - Buffer = (char*)Buffer + Amount; - Size -= Amount; - } - } -} - -/// childStopped - This method is called whenever the child has stopped -/// execution due to a breakpoint, step command, interruption, or whatever. -/// This stops the process, responds to any requests from the debugger, and when -/// commanded to, can continue execution by returning. -/// -void Child::childStopped() { - // Since we stopped, notify the parent that we did so. - char Token = 0; - writeToParent(&Token, sizeof(char)); - - StopAtNextOpportunity = false; - StopWhenSubprogramReturns = 0; - - // Now that the debugger knows that we stopped, read commands from it and - // respond to them appropriately. - CommandID Command; - while (1) { - SourceRegion *Frame; - const void *Result; - readFromParent(&Command, sizeof(CommandID)); - - switch (Command) { - case StepProgram: - // To step the program, just return. - StopAtNextOpportunity = true; - return; - - case FinishProgram: // Run until exit from the specified function... - readFromParent(&Frame, sizeof(Frame)); - // The user wants us to stop when the specified FUNCTION exits, not when - // the specified REGION exits. - StopWhenSubprogramReturns = Frame->getSubprogram(); - return; - - case ContProgram: - // To continue, just return back to execution. - return; - - case GetSubprogramDescriptor: - readFromParent(&Frame, sizeof(Frame)); - Result = - EE->getGlobalValueAtAddress(Frame->getSubprogram()->getDescriptor()); - writeToParent(&Result, sizeof(Result)); - break; - - case GetParentFrame: - readFromParent(&Frame, sizeof(Frame)); - Result = Frame ? Frame->getSubprogram()->getParent() : RegionStack; - writeToParent(&Result, sizeof(Result)); - break; - - case GetFrameLocation: { - readFromParent(&Frame, sizeof(Frame)); - LocationToken LT = Frame->getLocation(EE); - writeToParent(<, sizeof(LT)); - break; - } - case AddBreakpoint: { - LocationToken Loc; - readFromParent(&Loc, sizeof(Loc)); - // Convert the GlobalVariable pointer to the address it was emitted to. - Loc.File = (GlobalVariable*)EE->getPointerToGlobal(Loc.File); - unsigned ID = CurBreakpoint++; - Breakpoints.push_back(std::make_pair(ID, Loc)); - writeToParent(&ID, sizeof(ID)); - break; - } - case RemoveBreakpoint: { - unsigned ID = 0; - readFromParent(&ID, sizeof(ID)); - for (unsigned i = 0, e = Breakpoints.size(); i != e; ++i) - if (Breakpoints[i].first == ID) { - Breakpoints.erase(Breakpoints.begin()+i); - break; - } - break; - } - default: - assert(0 && "Unknown command!"); - } - } -} - - - -/// startSubprogram - This method creates a new region for the subroutine -/// with the specified descriptor. -void Child::startSubprogram(void *SPDesc) { - RegionStack = new SourceSubprogram(RegionStack, SPDesc); -} - -/// startRegion - This method initiates the creation of an anonymous region. -/// -void Child::startRegion() { - RegionStack = new SourceRegion(RegionStack); -} - -/// endRegion - This method terminates the last active region. -/// -void Child::endRegion() { - SourceRegion *R = RegionStack->getParent(); - - // If the debugger wants us to stop when this frame is destroyed, do so. - if (RegionStack == StopWhenSubprogramReturns) { - StopAtNextOpportunity = true; - StopWhenSubprogramReturns = 0; - } - - delete RegionStack; - RegionStack = R; -} - - - - -/// reachedLine - This method is automatically called by the program every time -/// it executes an llvm.dbg.stoppoint intrinsic. If the debugger wants us to -/// stop here, we do so, otherwise we continue execution. Note that the Data -/// pointer coming in is a pointer to the LLVM global variable that represents -/// the source file we are in. We do not use the contents of the global -/// directly in the child, but we do use its address. -/// -void Child::reachedLine(unsigned Line, unsigned Col, void *SourceDesc) { - if (RegionStack) - RegionStack->updateLocation(Line, Col, SourceDesc); - - // If we hit a breakpoint, stop the program. - for (unsigned i = 0, e = Breakpoints.size(); i != e; ++i) - if (Line == Breakpoints[i].second.Line && - SourceDesc == (void*)Breakpoints[i].second.File && - Col == Breakpoints[i].second.Col) { - childStopped(); - return; - } - - // If we are single stepping the program, make sure to stop it. - if (StopAtNextOpportunity) - childStopped(); -} - - - - -//===----------------------------------------------------------------------===// -// Child class wrapper functions -// -// These functions are invoked directly by the program as it executes, in place -// of the debugging intrinsic functions that it contains. -// - - -/// llvm_debugger_stop - Every time the program reaches a new source line, it -/// will call back to this function. If the debugger has a breakpoint or -/// otherwise wants us to stop on this line, we do so, and notify the debugger -/// over the pipe. -/// -extern "C" -void *llvm_debugger_stop(void *Dummy, unsigned Line, unsigned Col, - void *SourceDescriptor) { - TheChild->reachedLine(Line, Col, SourceDescriptor); - return Dummy; -} - - -/// llvm_dbg_region_start - This function is invoked every time an anonymous -/// region of the source program is entered. -/// -extern "C" -void *llvm_dbg_region_start(void *Dummy) { - TheChild->startRegion(); - return Dummy; -} - -/// llvm_dbg_subprogram - This function is invoked every time a source-language -/// subprogram has been entered. -/// -extern "C" -void *llvm_dbg_subprogram(void *FuncDesc) { - TheChild->startSubprogram(FuncDesc); - return 0; -} - -/// llvm_dbg_region_end - This function is invoked every time a source-language -/// region (started with llvm.dbg.region.start or llvm.dbg.func.start) is -/// terminated. -/// -extern "C" -void llvm_dbg_region_end(void *Dummy) { - TheChild->endRegion(); -} - - - - -namespace { - /// DebuggerIntrinsicLowering - This class implements a simple intrinsic - /// lowering class that revectors debugging intrinsics to call actual - /// functions (defined above), instead of being turned into noops. - struct DebuggerIntrinsicLowering : public DefaultIntrinsicLowering { - DebuggerIntrinsicLowering() { ShouldEmitDebugFunctions = true; } - virtual void LowerIntrinsicCall(CallInst *CI) { - Module *M = CI->getParent()->getParent()->getParent(); - switch (CI->getCalledFunction()->getIntrinsicID()) { - case Intrinsic::dbg_stoppoint: - // Turn call into a call to llvm_debugger_stop - CI->setOperand(0, M->getOrInsertFunction("llvm_debugger_stop", - CI->getCalledFunction()->getFunctionType())); - break; - case Intrinsic::dbg_region_start: - // Turn call into a call to llvm_dbg_region_start - CI->setOperand(0, M->getOrInsertFunction("llvm_dbg_region_start", - CI->getCalledFunction()->getFunctionType())); - break; - - case Intrinsic::dbg_region_end: - // Turn call into a call to llvm_dbg_region_end - CI->setOperand(0, M->getOrInsertFunction("llvm_dbg_region_end", - CI->getCalledFunction()->getFunctionType())); - break; - case Intrinsic::dbg_func_start: - // Turn call into a call to llvm_dbg_subprogram - CI->setOperand(0, M->getOrInsertFunction("llvm_dbg_subprogram", - CI->getCalledFunction()->getFunctionType())); - break; - default: - DefaultIntrinsicLowering::LowerIntrinsicCall(CI); - break; - } - } - }; -} // end anonymous namespace - - -static void runChild(Module *M, const std::vector<std::string> &Arguments, - const char * const *envp, - FDHandle ReadFD, FDHandle WriteFD) { - - // Create an execution engine that uses our custom intrinsic lowering object - // to revector debugging intrinsic functions into actual functions defined - // above. - ExecutionEngine *EE = - ExecutionEngine::create(new ExistingModuleProvider(M), false, - new DebuggerIntrinsicLowering()); - assert(EE && "Couldn't create an ExecutionEngine, not even an interpreter?"); - - // Call the main function from M as if its signature were: - // int main (int argc, char **argv, const char **envp) - // using the contents of Args to determine argc & argv, and the contents of - // EnvVars to determine envp. - // - Function *Fn = M->getMainFunction(); - if (!Fn) exit(1); - - // Create the child class instance which will be used by the debugger - // callbacks to keep track of the current state of the process. - assert(TheChild == 0 && "A child process has already been created??"); - TheChild = new Child(M, EE, ReadFD, WriteFD); - - // Run main... - int Result = EE->runFunctionAsMain(Fn, Arguments, envp); - - // If the program didn't explicitly call exit, call exit now, for the program. - // This ensures that any atexit handlers get called correctly. - Function *Exit = M->getOrInsertFunction("exit", Type::VoidTy, Type::IntTy, - (Type *)0); - - std::vector<GenericValue> Args; - GenericValue ResultGV; - ResultGV.IntVal = Result; - Args.push_back(ResultGV); - EE->runFunction(Exit, Args); - abort(); -} |