From 2eacf26aa61038c5171f7b0257bfc1d2fbf87f8d Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 5 Jan 2004 05:25:10 +0000 Subject: [PATCH] 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 --- lib/Debugger/Debugger.cpp | 212 +++++ lib/Debugger/Makefile | 13 + lib/Debugger/ProgramInfo.cpp | 409 ++++++++++ lib/Debugger/README.txt | 8 + lib/Debugger/RuntimeInfo.cpp | 69 ++ lib/Debugger/SourceFile.cpp | 116 +++ lib/Debugger/SourceLanguage-CFamily.cpp | 28 + lib/Debugger/SourceLanguage-CPlusPlus.cpp | 27 + lib/Debugger/SourceLanguage-Unknown.cpp | 136 ++++ lib/Debugger/SourceLanguage.cpp | 54 ++ lib/Debugger/UnixLocalInferiorProcess.cpp | 911 ++++++++++++++++++++++ 11 files changed, 1983 insertions(+) create mode 100644 lib/Debugger/Debugger.cpp create mode 100644 lib/Debugger/Makefile create mode 100644 lib/Debugger/ProgramInfo.cpp create mode 100644 lib/Debugger/README.txt create mode 100644 lib/Debugger/RuntimeInfo.cpp create mode 100644 lib/Debugger/SourceFile.cpp create mode 100644 lib/Debugger/SourceLanguage-CFamily.cpp create mode 100644 lib/Debugger/SourceLanguage-CPlusPlus.cpp create mode 100644 lib/Debugger/SourceLanguage-Unknown.cpp create mode 100644 lib/Debugger/SourceLanguage.cpp create mode 100644 lib/Debugger/UnixLocalInferiorProcess.cpp diff --git a/lib/Debugger/Debugger.cpp b/lib/Debugger/Debugger.cpp new file mode 100644 index 00000000000..fd11f23b37b --- /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 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 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())); + } +} diff --git a/lib/Debugger/Makefile b/lib/Debugger/Makefile new file mode 100644 index 00000000000..1cb6e0d21d4 --- /dev/null +++ b/lib/Debugger/Makefile @@ -0,0 +1,13 @@ +##===- lib/Debugger/Makefile -------------------------------*- Makefile -*-===## +# +# 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. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. +LIBRARYNAME = debugger + +include $(LEVEL)/Makefile.common diff --git a/lib/Debugger/ProgramInfo.cpp b/lib/Debugger/ProgramInfo.cpp new file mode 100644 index 00000000000..46584a942c9 --- /dev/null +++ b/lib/Debugger/ProgramInfo.cpp @@ -0,0 +1,409 @@ +//===-- ProgramInfo.cpp - Compute and cache info about a program ----------===// +// +// 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 implements the ProgramInfo and related classes, by sorting through +// the loaded Module. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Debugger/ProgramInfo.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Intrinsics.h" +#include "llvm/iOther.h" +#include "llvm/Module.h" +#include "llvm/Debugger/SourceFile.h" +#include "llvm/Debugger/SourceLanguage.h" +#include "Support/FileUtilities.h" +#include "Support/SlowOperationInformer.h" +#include "Support/STLExtras.h" +using namespace llvm; + +/// getGlobalVariablesUsing - Return all of the global variables which have the +/// specified value in their initializer somewhere. +static void getGlobalVariablesUsing(Value *V, + std::vector &Found) { + for (Value::use_iterator I = V->use_begin(), E = V->use_end(); I != E; ++I) { + if (GlobalVariable *GV = dyn_cast(*I)) + Found.push_back(GV); + else if (Constant *C = dyn_cast(*I)) + getGlobalVariablesUsing(C, Found); + } +} + +/// getStringValue - Turn an LLVM constant pointer that eventually points to a +/// global into a string value. Return an empty string if we can't do it. +/// +static std::string getStringValue(Value *V, unsigned Offset = 0) { + if (GlobalVariable *GV = dyn_cast(V)) { + if (GV->hasInitializer() && isa(GV->getInitializer())) { + ConstantArray *Init = cast(GV->getInitializer()); + if (Init->getType()->getElementType() == Type::SByteTy || + Init->getType()->getElementType() == Type::UByteTy) { + std::string Result = Init->getAsString(); + if (Offset < Result.size()) { + // If we are pointing INTO The string, erase the beginning... + Result.erase(Result.begin(), Result.begin()+Offset); + + // Take off the null terminator, and any string fragments after it. + std::string::size_type NullPos = Result.find_first_of((char)0); + if (NullPos != std::string::npos) + Result.erase(Result.begin()+NullPos, Result.end()); + return Result; + } + } + } + } else if (Constant *C = dyn_cast(V)) { + if (ConstantPointerRef *CPR = dyn_cast(C)) + return getStringValue(CPR->getValue(), Offset); + else if (ConstantExpr *CE = dyn_cast(C)) { + if (CE->getOpcode() == Instruction::GetElementPtr) { + // Turn a gep into the specified offset. + if (CE->getNumOperands() == 3 && + cast(CE->getOperand(1))->isNullValue() && + isa(CE->getOperand(2))) { + return getStringValue(CE->getOperand(0), + Offset+cast(CE->getOperand(2))->getRawValue()); + } + } + } + } + return ""; +} + +/// getNextStopPoint - Follow the def-use chains of the specified LLVM value, +/// traversing the use chains until we get to a stoppoint. When we do, return +/// the source location of the stoppoint. If we don't find a stoppoint, return +/// null. +static const GlobalVariable *getNextStopPoint(const Value *V, unsigned &LineNo, + unsigned &ColNo) { + // The use-def chains can fork. As such, we pick the lowest numbered one we + // find. + const GlobalVariable *LastDesc = 0; + unsigned LastLineNo = ~0; + unsigned LastColNo = ~0; + + for (Value::use_const_iterator UI = V->use_begin(), E = V->use_end(); + UI != E; ++UI) { + bool ShouldRecurse = true; + if (cast(*UI)->getOpcode() == Instruction::PHI) { + // Infinite loops == bad, ignore PHI nodes. + ShouldRecurse = false; + } else if (const CallInst *CI = dyn_cast(*UI)) { + // If we found a stop point, check to see if it is earlier than what we + // already have. If so, remember it. + if (const Function *F = CI->getCalledFunction()) + if (F->getIntrinsicID() == Intrinsic::dbg_stoppoint) { + unsigned CurLineNo = ~0, CurColNo = ~0; + const GlobalVariable *CurDesc = 0; + if (const ConstantInt *C = dyn_cast(CI->getOperand(2))) + CurLineNo = C->getRawValue(); + if (const ConstantInt *C = dyn_cast(CI->getOperand(3))) + CurColNo = C->getRawValue(); + const Value *Op = CI->getOperand(4); + if (const ConstantPointerRef *CPR = dyn_cast(Op)) + Op = CPR->getValue(); + + if ((CurDesc = dyn_cast(Op)) && + (LineNo < LastLineNo || + (LineNo == LastLineNo && ColNo < LastColNo))) { + LastDesc = CurDesc; + LastLineNo = CurLineNo; + LastColNo = CurColNo; + } + ShouldRecurse = false; + } + + } + + // If this is not a phi node or a stopping point, recursively scan the users + // of this instruction to skip over region.begin's and the like. + if (ShouldRecurse) { + unsigned CurLineNo, CurColNo; + if (const GlobalVariable *GV = getNextStopPoint(*UI, CurLineNo,CurColNo)){ + if (LineNo < LastLineNo || (LineNo == LastLineNo && ColNo < LastColNo)){ + LastDesc = GV; + LastLineNo = CurLineNo; + LastColNo = CurColNo; + } + } + } + } + + if (LastDesc) { + LineNo = LastLineNo != ~0U ? LastLineNo : 0; + ColNo = LastColNo != ~0U ? LastColNo : 0; + } + return LastDesc; +} + + +//===----------------------------------------------------------------------===// +// SourceFileInfo implementation +// + +SourceFileInfo::SourceFileInfo(const GlobalVariable *Desc, + const SourceLanguage &Lang) + : Language(&Lang), Descriptor(Desc) { + Version = 0; + SourceText = 0; + + if (Desc && Desc->hasInitializer()) + if (ConstantStruct *CS = dyn_cast(Desc->getInitializer())) + if (CS->getNumOperands() > 4) { + if (ConstantUInt *CUI = dyn_cast(CS->getOperand(1))) + Version = CUI->getValue(); + + BaseName = getStringValue(CS->getOperand(3)); + Directory = getStringValue(CS->getOperand(4)); + } +} + +SourceFileInfo::~SourceFileInfo() { + delete SourceText; +} + +SourceFile &SourceFileInfo::getSourceText() const { + // FIXME: this should take into account the source search directories! + if (SourceText == 0) // Read the file in if we haven't already. + if (!Directory.empty() && FileOpenable(Directory+"/"+BaseName)) + SourceText = new SourceFile(Directory+"/"+BaseName, Descriptor); + else + SourceText = new SourceFile(BaseName, Descriptor); + return *SourceText; +} + + +//===----------------------------------------------------------------------===// +// SourceFunctionInfo implementation +// +SourceFunctionInfo::SourceFunctionInfo(ProgramInfo &PI, + const GlobalVariable *Desc) + : Descriptor(Desc) { + LineNo = ColNo = 0; + if (Desc && Desc->hasInitializer()) + if (ConstantStruct *CS = dyn_cast(Desc->getInitializer())) + if (CS->getNumOperands() > 2) { + // Entry #1 is the file descriptor. + if (const ConstantPointerRef *CPR = + dyn_cast(CS->getOperand(1))) + if (const GlobalVariable *GV = + dyn_cast(CPR->getValue())) + SourceFile = &PI.getSourceFile(GV); + + // Entry #2 is the function name. + Name = getStringValue(CS->getOperand(2)); + } +} + +/// getSourceLocation - This method returns the location of the first stopping +/// point in the function. +void SourceFunctionInfo::getSourceLocation(unsigned &RetLineNo, + unsigned &RetColNo) const { + // If we haven't computed this yet... + if (!LineNo) { + // Look at all of the users of the function descriptor, looking for calls to + // %llvm.dbg.func.start. + for (Value::use_const_iterator UI = Descriptor->use_begin(), + E = Descriptor->use_end(); UI != E; ++UI) + if (const CallInst *CI = dyn_cast(*UI)) + if (const Function *F = CI->getCalledFunction()) + if (F->getIntrinsicID() == Intrinsic::dbg_func_start) { + // We found the start of the function. Check to see if there are + // any stop points on the use-list of the function start. + const GlobalVariable *SD = getNextStopPoint(CI, LineNo, ColNo); + if (SD) { // We found the first stop point! + // This is just a sanity check. + if (getSourceFile().getDescriptor() != SD) + std::cout << "WARNING: first line of function is not in the" + " file that the function descriptor claims it is in.\n"; + break; + } + } + } + RetLineNo = LineNo; RetColNo = ColNo; +} + +//===----------------------------------------------------------------------===// +// ProgramInfo implementation +// + +ProgramInfo::ProgramInfo(Module *m) : M(m) { + assert(M && "Cannot create program information with a null module!"); + ProgramTimeStamp = getFileTimestamp(M->getModuleIdentifier()); + + SourceFilesIsComplete = false; + SourceFunctionsIsComplete = false; +} + +ProgramInfo::~ProgramInfo() { + // Delete cached information about source program objects... + for (std::map::iterator + I = SourceFiles.begin(), E = SourceFiles.end(); I != E; ++I) + delete I->second; + for (std::map::iterator + I = SourceFunctions.begin(), E = SourceFunctions.end(); I != E; ++I) + delete I->second; + + // Delete the source language caches. + for (unsigned i = 0, e = LanguageCaches.size(); i != e; ++i) + delete LanguageCaches[i].second; +} + + +//===----------------------------------------------------------------------===// +// SourceFileInfo tracking... +// + +/// getSourceFile - Return source file information for the specified source file +/// descriptor object, adding it to the collection as needed. This method +/// always succeeds (is unambiguous), and is always efficient. +/// +const SourceFileInfo & +ProgramInfo::getSourceFile(const GlobalVariable *Desc) { + SourceFileInfo *&Result = SourceFiles[Desc]; + if (Result) return *Result; + + // Figure out what language this source file comes from... + unsigned LangID = 0; // Zero is unknown language + if (Desc && Desc->hasInitializer()) + if (ConstantStruct *CS = dyn_cast(Desc->getInitializer())) + if (CS->getNumOperands() > 2) + if (ConstantUInt *CUI = dyn_cast(CS->getOperand(2))) + LangID = CUI->getValue(); + + const SourceLanguage &Lang = SourceLanguage::get(LangID); + SourceFileInfo *New = Lang.createSourceFileInfo(Desc, *this); + + // FIXME: this should check to see if there is already a Filename/WorkingDir + // pair that matches this one. If so, we shouldn't create the duplicate! + // + SourceFileIndex.insert(std::make_pair(New->getBaseName(), New)); + return *(Result = New); +} + + +/// getSourceFiles - Index all of the source files in the program and return +/// a mapping of it. This information is lazily computed the first time +/// that it is requested. Since this information can take a long time to +/// compute, the user is given a chance to cancel it. If this occurs, an +/// exception is thrown. +const std::map & +ProgramInfo::getSourceFiles(bool RequiresCompleteMap) { + // If we have a fully populated map, or if the client doesn't need one, just + // return what we have. + if (SourceFilesIsComplete || !RequiresCompleteMap) + return SourceFiles; + + // Ok, all of the source file descriptors (compile_unit in dwarf terms), + // should be on the use list of the llvm.dbg.translation_units global. + // + GlobalVariable *Units = + M->getGlobalVariable("llvm.dbg.translation_units", + StructType::get(std::vector())); + if (Units == 0) + throw "Program contains no debugging information!"; + + std::vector TranslationUnits; + getGlobalVariablesUsing(Units, TranslationUnits); + + SlowOperationInformer SOI("building source files index"); + + // Loop over all of the translation units found, building the SourceFiles + // mapping. + for (unsigned i = 0, e = TranslationUnits.size(); i != e; ++i) { + getSourceFile(TranslationUnits[i]); + SOI.progress(i+1, e); + } + + // Ok, if we got this far, then we indexed the whole program. + SourceFilesIsComplete = true; + return SourceFiles; +} + +/// getSourceFile - Look up the file with the specified name. If there is +/// more than one match for the specified filename, prompt the user to pick +/// one. If there is no source file that matches the specified name, throw +/// an exception indicating that we can't find the file. Otherwise, return +/// the file information for that file. +const SourceFileInfo &ProgramInfo::getSourceFile(const std::string &Filename) { + std::multimap::const_iterator Start, End; + getSourceFiles(); + tie(Start, End) = SourceFileIndex.equal_range(Filename); + + if (Start == End) throw "Could not find source file '" + Filename + "'!"; + const SourceFileInfo &SFI = *Start->second; + ++Start; + if (Start == End) return SFI; + + throw "FIXME: Multiple source files with the same name not implemented!"; +} + + +//===----------------------------------------------------------------------===// +// SourceFunctionInfo tracking... +// + + +/// getFunction - Return function information for the specified function +/// descriptor object, adding it to the collection as needed. This method +/// always succeeds (is unambiguous), and is always efficient. +/// +const SourceFunctionInfo & +ProgramInfo::getFunction(const GlobalVariable *Desc) { + SourceFunctionInfo *&Result = SourceFunctions[Desc]; + if (Result) return *Result; + + // Figure out what language this function comes from... + const GlobalVariable *SourceFileDesc = 0; + if (Desc && Desc->hasInitializer()) + if (ConstantStruct *CS = dyn_cast(Desc->getInitializer())) + if (CS->getNumOperands() > 0) + if (const ConstantPointerRef *CPR = + dyn_cast(CS->getOperand(1))) + SourceFileDesc = dyn_cast(CPR->getValue()); + + const SourceLanguage &Lang = getSourceFile(SourceFileDesc).getLanguage(); + return *(Result = Lang.createSourceFunctionInfo(Desc, *this)); +} + + +// getSourceFunctions - Index all of the functions in the program and return +// them. This information is lazily computed the first time that it is +// requested. Since this information can take a long time to compute, the user +// is given a chance to cancel it. If this occurs, an exception is thrown. +const std::map & +ProgramInfo::getSourceFunctions(bool RequiresCompleteMap) { + if (SourceFunctionsIsComplete || !RequiresCompleteMap) + return SourceFunctions; + + // Ok, all of the source function descriptors (subprogram in dwarf terms), + // should be on the use list of the llvm.dbg.translation_units global. + // + GlobalVariable *Units = + M->getGlobalVariable("llvm.dbg.globals", + StructType::get(std::vector())); + if (Units == 0) + throw "Program contains no debugging information!"; + + std::vector Functions; + getGlobalVariablesUsing(Units, Functions); + + SlowOperationInformer SOI("building functions index"); + + // Loop over all of the functions found, building the SourceFunctions mapping. + for (unsigned i = 0, e = Functions.size(); i != e; ++i) { + getFunction(Functions[i]); + SOI.progress(i+1, e); + } + + // Ok, if we got this far, then we indexed the whole program. + SourceFunctionsIsComplete = true; + return SourceFunctions; +} diff --git a/lib/Debugger/README.txt b/lib/Debugger/README.txt new file mode 100644 index 00000000000..04a18febc65 --- /dev/null +++ b/lib/Debugger/README.txt @@ -0,0 +1,8 @@ +//===-- llvm/lib/Debugger/ - LLVM Debugger interfaces ---------------------===// + +This directory contains the implementation of the LLVM debugger backend. This +directory builds into a library which can be used by various debugger +front-ends to debug LLVM programs. The current command line LLVM debugger, +llvm-db is currently the only client of this library, but others could be +built, to provide a GUI front-end for example. + diff --git a/lib/Debugger/RuntimeInfo.cpp b/lib/Debugger/RuntimeInfo.cpp new file mode 100644 index 00000000000..d1bc48b543a --- /dev/null +++ b/lib/Debugger/RuntimeInfo.cpp @@ -0,0 +1,69 @@ +//===-- RuntimeInfo.cpp - Compute and cache info about running program ----===// +// +// 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 implements the RuntimeInfo and related classes, by querying and +// cachine information from the running inferior process. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Debugger/InferiorProcess.h" +#include "llvm/Debugger/ProgramInfo.h" +#include "llvm/Debugger/RuntimeInfo.h" +using namespace llvm; + +//===----------------------------------------------------------------------===// +// StackFrame class implementation + +StackFrame::StackFrame(RuntimeInfo &ri, void *ParentFrameID) + : RI(ri), SourceInfo(0) { + FrameID = RI.getInferiorProcess().getPreviousFrame(ParentFrameID); + if (FrameID == 0) throw "Stack frame does not exist!"; + + // Compute lazily as needed. + FunctionDesc = 0; +} + +const GlobalVariable *StackFrame::getFunctionDesc() { + if (FunctionDesc == 0) + FunctionDesc = RI.getInferiorProcess().getSubprogramDesc(FrameID); + return FunctionDesc; +} + +/// getSourceLocation - Return the source location that this stack frame is +/// sitting at. +void StackFrame::getSourceLocation(unsigned &lineNo, unsigned &colNo, + const SourceFileInfo *&sourceInfo) { + if (SourceInfo == 0) { + const GlobalVariable *SourceDesc = 0; + RI.getInferiorProcess().getFrameLocation(FrameID, LineNo,ColNo, SourceDesc); + SourceInfo = &RI.getProgramInfo().getSourceFile(SourceDesc); + } + + lineNo = LineNo; + colNo = ColNo; + sourceInfo = SourceInfo; +} + +//===----------------------------------------------------------------------===// +// RuntimeInfo class implementation + +/// materializeFrame - Create and process all frames up to and including the +/// specified frame number. This throws an exception if the specified frame +/// ID is nonexistant. +void RuntimeInfo::materializeFrame(unsigned ID) { + assert(ID >= CallStack.size() && "no need to materialize this frame!"); + void *CurFrame = 0; + if (!CallStack.empty()) + CurFrame = CallStack.back().getFrameID(); + + while (CallStack.size() <= ID) { + CallStack.push_back(StackFrame(*this, CurFrame)); + CurFrame = CallStack.back().getFrameID(); + } +} diff --git a/lib/Debugger/SourceFile.cpp b/lib/Debugger/SourceFile.cpp new file mode 100644 index 00000000000..2eb3b5750b1 --- /dev/null +++ b/lib/Debugger/SourceFile.cpp @@ -0,0 +1,116 @@ +//===-- SourceFile.cpp - SourceFile implementation for the debugger -------===// +// +// 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 implements the SourceFile class for the LLVM debugger. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Debugger/SourceFile.h" +#include "Support/SlowOperationInformer.h" +#include "Support/FileUtilities.h" +#include +#include +#include +#include +using namespace llvm; + +/// readFile - Load Filename into FileStart and FileEnd. +/// +void SourceFile::readFile() { + ssize_t FileSize = getFileSize(Filename); + if (FileSize != -1) { + FDHandle FD(open(Filename.c_str(), O_RDONLY)); + if (FD != -1) { + char *FilePos = new char[FileSize]; + FileStart = FilePos; + + // If this takes a long time, inform the user what we are doing. + SlowOperationInformer SOI("loading source file '" + Filename + "'"); + + try { + // Read in the whole buffer. + unsigned Amount = FileSize; + while (Amount) { + unsigned AmountToRead = 512*1024; + if (Amount < AmountToRead) AmountToRead = Amount; + ssize_t ReadAmount = read(FD, FilePos, AmountToRead); + if (ReadAmount < 0 && errno == EINTR) + continue; + else if (ReadAmount <= 0) { + // Couldn't read whole file just free memory and continue. + throw "Error reading file '" + Filename + "'!"; + } + Amount -= ReadAmount; + FilePos += ReadAmount; + + SOI.progress(FileSize-Amount, FileSize); + } + + } catch (const std::string &Msg) { + std::cout << Msg << "\n"; + // If the user cancels the operation, clean up after ourselves. + delete [] FileStart; + FileStart = 0; + return; + } + + FileEnd = FileStart+FileSize; + } + } +} + +/// calculateLineOffsets - Compute the LineOffset vector for the current file. +/// +void SourceFile::calculateLineOffsets() const { + assert(LineOffset.empty() && "Line offsets already computed!"); + const char *BufPtr = FileStart; + do { + LineOffset.push_back(BufPtr-FileStart); + + // Scan until we get to a newline. + while (BufPtr != FileEnd && *BufPtr != '\n' && *BufPtr != '\r') + ++BufPtr; + + if (BufPtr != FileEnd) { + ++BufPtr; // Skip over the \n or \r + if (BufPtr[-1] == '\r' && BufPtr != FileEnd && BufPtr[0] == '\n') + ++BufPtr; // Skip over dos/windows style \r\n's + } + } while (BufPtr != FileEnd); +} + + +/// getSourceLine - Given a line number, return the start and end of the line +/// in the file. If the line number is invalid, or if the file could not be +/// loaded, null pointers are returned for the start and end of the file. Note +/// that line numbers start with 0, not 1. +void SourceFile::getSourceLine(unsigned LineNo, const char *&LineStart, + const char *&LineEnd) const { + LineStart = LineEnd = 0; + if (FileStart == 0) return; // Couldn't load file, return null pointers + if (LineOffset.empty()) calculateLineOffsets(); + + // Asking for an out-of-range line number? + if (LineNo >= LineOffset.size()) return; + + // Otherwise, they are asking for a valid line, which we can fulfill. + LineStart = FileStart+LineOffset[LineNo]; + + if (LineNo+1 < LineOffset.size()) + LineEnd = FileStart+LineOffset[LineNo+1]; + else + LineEnd = FileEnd; + + // If the line ended with a newline, strip it off. + while (LineEnd != LineStart && (LineEnd[-1] == '\n' || LineEnd[-1] == '\r')) + --LineEnd; + + assert(LineEnd >= LineStart && "We somehow got our pointers swizzled!"); +} + diff --git a/lib/Debugger/SourceLanguage-CFamily.cpp b/lib/Debugger/SourceLanguage-CFamily.cpp new file mode 100644 index 00000000000..392c07c818e --- /dev/null +++ b/lib/Debugger/SourceLanguage-CFamily.cpp @@ -0,0 +1,28 @@ +//===-- SourceLanguage-CFamily.cpp - C family SourceLanguage impl ---------===// +// +// 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 implements the SourceLanguage class for the C family of languages +// (K&R C, C89, C99, etc). +// +//===----------------------------------------------------------------------===// + +#include "llvm/Debugger/SourceLanguage.h" +using namespace llvm; + +#if 0 +namespace { + struct CSL : public SourceLanguage { + } TheCSourceLanguageInstance; +} +#endif + +const SourceLanguage &SourceLanguage::getCFamilyInstance() { + return get(0); // We don't have an implementation for C yet fall back on + // generic +} diff --git a/lib/Debugger/SourceLanguage-CPlusPlus.cpp b/lib/Debugger/SourceLanguage-CPlusPlus.cpp new file mode 100644 index 00000000000..31e4fd43244 --- /dev/null +++ b/lib/Debugger/SourceLanguage-CPlusPlus.cpp @@ -0,0 +1,27 @@ +//===-- SourceLanguage-CPlusPlus.cpp - C++ SourceLanguage impl ------------===// +// +// 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 implements the SourceLanguage class for the C++ language. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Debugger/SourceLanguage.h" +using namespace llvm; + +#if 0 +namespace { + struct CPPSL : public SourceLanguage { + } TheCPlusPlusLanguageInstance; +} +#endif + +const SourceLanguage &SourceLanguage::getCPlusPlusInstance() { + return get(0); // We don't have an implementation for C yet fall back on + // generic +} diff --git a/lib/Debugger/SourceLanguage-Unknown.cpp b/lib/Debugger/SourceLanguage-Unknown.cpp new file mode 100644 index 00000000000..7d262d1131e --- /dev/null +++ b/lib/Debugger/SourceLanguage-Unknown.cpp @@ -0,0 +1,136 @@ +//===-- SourceLanguage-Unknown.cpp - Implement itf for unknown languages --===// +// +// 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. +// +//===----------------------------------------------------------------------===// +// +// If the LLVM debugger does not have a module for a particular language, it +// falls back on using this one to perform the source-language interface. This +// interface is not wonderful, but it gets the job done. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Debugger/SourceLanguage.h" +#include "llvm/Debugger/ProgramInfo.h" +#include +using namespace llvm; + +//===----------------------------------------------------------------------===// +// Implement the SourceLanguage cache for the Unknown language. +// + +namespace { + /// SLUCache - This cache allows for efficient lookup of source functions by + /// name. + /// + struct SLUCache : public SourceLanguageCache { + ProgramInfo &PI; + std::multimap FunctionMap; + public: + SLUCache(ProgramInfo &pi); + + typedef std::multimap::const_iterator + fm_iterator; + + std::pair + getFunction(const std::string &Name) const { + return FunctionMap.equal_range(Name); + } + + SourceFunctionInfo *addSourceFunction(SourceFunctionInfo *SF) { + FunctionMap.insert(std::make_pair(SF->getSymbolicName(), SF)); + return SF; + } + }; +} + +SLUCache::SLUCache(ProgramInfo &pi) : PI(pi) { +} + + +//===----------------------------------------------------------------------===// +// Implement SourceLanguageUnknown class, which is used to handle unrecognized +// languages. +// + +namespace { + struct SLU : public SourceLanguage { + //===------------------------------------------------------------------===// + // Implement the miscellaneous methods... + // + virtual const char *getSourceLanguageName() const { + return "unknown"; + } + + /// lookupFunction - Given a textual function name, return the + /// SourceFunctionInfo descriptor for that function, or null if it cannot be + /// found. If the program is currently running, the RuntimeInfo object + /// provides information about the current evaluation context, otherwise it + /// will be null. + /// + virtual SourceFunctionInfo *lookupFunction(const std::string &FunctionName, + ProgramInfo &PI, + RuntimeInfo *RI = 0) const; + + //===------------------------------------------------------------------===// + // We do use a cache for information... + // + typedef SLUCache CacheType; + SLUCache *createSourceLanguageCache(ProgramInfo &PI) const { + return new SLUCache(PI); + } + + /// createSourceFunctionInfo - Create the new object and inform the cache of + /// the new function. + virtual SourceFunctionInfo * + createSourceFunctionInfo(const GlobalVariable *Desc, ProgramInfo &PI) const; + + } TheUnknownSourceLanguageInstance; +} + +const SourceLanguage &SourceLanguage::getUnknownLanguageInstance() { + return TheUnknownSourceLanguageInstance; +} + + +SourceFunctionInfo * +SLU::createSourceFunctionInfo(const GlobalVariable *Desc, + ProgramInfo &PI) const { + SourceFunctionInfo *Result = new SourceFunctionInfo(PI, Desc); + return PI.getLanguageCache(this).addSourceFunction(Result); +} + + +/// lookupFunction - Given a textual function name, return the +/// SourceFunctionInfo descriptor for that function, or null if it cannot be +/// found. If the program is currently running, the RuntimeInfo object +/// provides information about the current evaluation context, otherwise it will +/// be null. +/// +SourceFunctionInfo *SLU::lookupFunction(const std::string &FunctionName, + ProgramInfo &PI, RuntimeInfo *RI) const{ + SLUCache &Cache = PI.getLanguageCache(this); + std::pair IP + = Cache.getFunction(FunctionName); + + if (IP.first == IP.second) { + if (PI.allSourceFunctionsRead()) + return 0; // Nothing found + + // Otherwise, we might be able to find the function if we read all of them + // in. Do so now. + PI.getSourceFunctions(); + assert(PI.allSourceFunctionsRead() && "Didn't read in all functions?"); + return lookupFunction(FunctionName, PI, RI); + } + + SourceFunctionInfo *Found = IP.first->second; + ++IP.first; + if (IP.first != IP.second) + std::cout << "Whoa, found multiple functions with the same name. I should" + << " ask the user which one to use: FIXME!\n"; + return Found; +} diff --git a/lib/Debugger/SourceLanguage.cpp b/lib/Debugger/SourceLanguage.cpp new file mode 100644 index 00000000000..70268445632 --- /dev/null +++ b/lib/Debugger/SourceLanguage.cpp @@ -0,0 +1,54 @@ +//===-- SourceLanguage.cpp - Implement the SourceLanguage class -----------===// +// +// 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 implements the SourceLanguage class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Debugger/SourceLanguage.h" +#include "llvm/Debugger/ProgramInfo.h" +using namespace llvm; + +const SourceLanguage &SourceLanguage::get(unsigned ID) { + switch (ID) { + case 1: // DW_LANG_C89 + case 2: // DW_LANG_C + case 12: // DW_LANG_C99 + return getCFamilyInstance(); + + case 4: // DW_LANG_C_plus_plus + return getCPlusPlusInstance(); + + case 3: // DW_LANG_Ada83 + case 5: // DW_LANG_Cobol74 + case 6: // DW_LANG_Cobol85 + case 7: // DW_LANG_Fortran77 + case 8: // DW_LANG_Fortran90 + case 9: // DW_LANG_Pascal83 + case 10: // DW_LANG_Modula2 + case 11: // DW_LANG_Java + case 13: // DW_LANG_Ada95 + case 14: // DW_LANG_Fortran95 + default: + return getUnknownLanguageInstance(); + } +} + + +SourceFileInfo * +SourceLanguage::createSourceFileInfo(const GlobalVariable *Desc, + ProgramInfo &PI) const { + return new SourceFileInfo(Desc, *this); +} + +SourceFunctionInfo * +SourceLanguage::createSourceFunctionInfo(const GlobalVariable *Desc, + ProgramInfo &PI) const { + return new SourceFunctionInfo(PI, Desc); +} diff --git a/lib/Debugger/UnixLocalInferiorProcess.cpp b/lib/Debugger/UnixLocalInferiorProcess.cpp new file mode 100644 index 00000000000..ff9a15a1ead --- /dev/null +++ b/lib/Debugger/UnixLocalInferiorProcess.cpp @@ -0,0 +1,911 @@ +//===-- 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 starts 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/IntrinsicLowering.h" +#include "llvm/Constant.h" +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/Type.h" +#include "llvm/iOther.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "Support/FileUtilities.h" +#include "Support/StringExtras.h" +#include +#include // Unix specific debugger support +#include +#include +using namespace llvm; + +// runChild - Entry point for the child process. +static void runChild(Module *M, const std::vector &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 an +// 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 &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 &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 &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 &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 &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!"); + + 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!"; + + // If the child process was already dead, then it died unexpectedly. + 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 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(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 > 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 { + 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_debugger_stop + 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_debugger_stop + 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 &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, 0); + + std::vector Args; + GenericValue ResultGV; + ResultGV.IntVal = Result; + Args.push_back(ResultGV); + EE->runFunction(Exit, Args); + abort(); +} -- 2.34.1