diff --git a/include/llvm/Target/TargetOptions.h b/include/llvm/Target/TargetOptions.h index b27d49630a1..8d52dadc285 100644 --- a/include/llvm/Target/TargetOptions.h +++ b/include/llvm/Target/TargetOptions.h @@ -103,6 +103,15 @@ namespace llvm { /// information should be emitted. extern bool SjLjExceptionHandling; + /// JITEmitDebugInfo - This flag indicates that the JIT should try to emit + /// debug information and notify a debugger about it. + extern bool JITEmitDebugInfo; + + /// JITEmitDebugInfoToDisk - This flag indicates that the JIT should write + /// the object files generated by the JITEmitDebugInfo flag to disk. This + /// flag is hidden and is only for debugging the debug info. + extern bool JITEmitDebugInfoToDisk; + /// UnwindTablesMandatory - This flag indicates that unwind tables should /// be emitted for all functions. extern bool UnwindTablesMandatory; diff --git a/lib/CodeGen/ELFWriter.h b/lib/CodeGen/ELFWriter.h index e44ab3f838e..b61b4848b65 100644 --- a/lib/CodeGen/ELFWriter.h +++ b/lib/CodeGen/ELFWriter.h @@ -28,6 +28,7 @@ namespace llvm { class ELFSection; struct ELFSym; class GlobalVariable; + class JITDebugRegisterer; class Mangler; class MachineCodeEmitter; class MachineConstantPoolEntry; @@ -51,6 +52,7 @@ namespace llvm { /// class ELFWriter : public MachineFunctionPass { friend class ELFCodeEmitter; + friend class JITDebugRegisterer; public: static char ID; diff --git a/lib/ExecutionEngine/JIT/JIT.cpp b/lib/ExecutionEngine/JIT/JIT.cpp index 37433504333..b2a268bce8b 100644 --- a/lib/ExecutionEngine/JIT/JIT.cpp +++ b/lib/ExecutionEngine/JIT/JIT.cpp @@ -234,7 +234,7 @@ JIT::JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji, jitstate = new JITState(MP); // Initialize JCE - JCE = createEmitter(*this, JMM); + JCE = createEmitter(*this, JMM, TM); // Add target data MutexGuard locked(lock); diff --git a/lib/ExecutionEngine/JIT/JIT.h b/lib/ExecutionEngine/JIT/JIT.h index e3ab9e21c9e..dfeffb516e1 100644 --- a/lib/ExecutionEngine/JIT/JIT.h +++ b/lib/ExecutionEngine/JIT/JIT.h @@ -185,7 +185,8 @@ public: void NotifyFreeingMachineCode(const Function &F, void *OldPtr); private: - static JITCodeEmitter *createEmitter(JIT &J, JITMemoryManager *JMM); + static JITCodeEmitter *createEmitter(JIT &J, JITMemoryManager *JMM, + TargetMachine &tm); void runJITOnFunctionUnlocked(Function *F, const MutexGuard &locked); void updateFunctionStub(Function *F); void updateDlsymStubTable(); diff --git a/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp b/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp new file mode 100644 index 00000000000..62d66d55ae0 --- /dev/null +++ b/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp @@ -0,0 +1,208 @@ +//===-- JITDebugRegisterer.cpp - Register debug symbols for JIT -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a JITDebugRegisterer object that is used by the JIT to +// register debug info with debuggers like GDB. +// +//===----------------------------------------------------------------------===// + +#include "JITDebugRegisterer.h" +#include "../../CodeGen/ELF.h" +#include "../../CodeGen/ELFWriter.h" +#include "llvm/LLVMContext.h" +#include "llvm/Function.h" +#include "llvm/Module.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/MutexGuard.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Mutex.h" +#include +#include + +namespace llvm { + +// This must be kept in sync with gdb/gdb/jit.h . +extern "C" { + + // Debuggers puts a breakpoint in this function. + void __attribute__((noinline)) __jit_debug_register_code() { } + + // We put information about the JITed function in this global, which the + // debugger reads. Make sure to specify the version statically, because the + // debugger checks the version before we can set it during runtime. + struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 }; + +} + +namespace { + + /// JITDebugLock - Used to serialize all code registration events, since they + /// modify global variables. + sys::Mutex JITDebugLock; + +} + +JITDebugRegisterer::JITDebugRegisterer(TargetMachine &tm) : TM(tm), FnMap() { } + +JITDebugRegisterer::~JITDebugRegisterer() { + // Free all ELF memory. + for (RegisteredFunctionsMap::iterator I = FnMap.begin(), E = FnMap.end(); + I != E; ++I) { + // Call the private method that doesn't update the map so our iterator + // doesn't break. + UnregisterFunctionInternal(I); + } + FnMap.clear(); +} + +std::string JITDebugRegisterer::MakeELF(const Function *F, DebugInfo &I) { + // Stack allocate an empty module with an empty LLVMContext for the ELFWriter + // API. We don't use the real module because then the ELFWriter would write + // out unnecessary GlobalValues during finalization. + LLVMContext Context; + Module M("", Context); + + // Make a buffer for the ELF in memory. + std::string Buffer; + raw_string_ostream O(Buffer); + ELFWriter EW(O, TM); + EW.doInitialization(M); + + // Copy the binary into the .text section. This isn't necessary, but it's + // useful to be able to disassemble the ELF by hand. + ELFSection &Text = EW.getTextSection((Function *)F); + Text.Addr = (uint64_t)I.FnStart; + // TODO: We could eliminate this copy if we somehow used a pointer/size pair + // instead of a vector. + Text.getData().assign(I.FnStart, I.FnEnd); + + // Copy the exception handling call frame information into the .eh_frame + // section. This allows GDB to get a good stack trace, particularly on + // linux x86_64. Mark this as a PROGBITS section that needs to be loaded + // into memory at runtime. + ELFSection &EH = EW.getSection(".eh_frame", ELFSection::SHT_PROGBITS, + ELFSection::SHF_ALLOC); + // Pointers in the DWARF EH info are all relative to the EH frame start, + // which is stored here. + EH.Addr = (uint64_t)I.EhStart; + // TODO: We could eliminate this copy if we somehow used a pointer/size pair + // instead of a vector. + EH.getData().assign(I.EhStart, I.EhEnd); + + // Add this single function to the symbol table, so the debugger prints the + // name instead of '???'. We give the symbol default global visibility. + ELFSym *FnSym = ELFSym::getGV(F, + ELFSym::STB_GLOBAL, + ELFSym::STT_FUNC, + ELFSym::STV_DEFAULT); + FnSym->SectionIdx = Text.SectionIdx; + FnSym->Size = I.FnEnd - I.FnStart; + FnSym->Value = 0; // Offset from start of section. + EW.SymbolList.push_back(FnSym); + + EW.doFinalization(M); + O.flush(); + + // When trying to debug why GDB isn't getting the debug info right, it's + // awfully helpful to write the object file to disk so that it can be + // inspected with readelf and objdump. + if (JITEmitDebugInfoToDisk) { + std::string Filename; + raw_string_ostream O2(Filename); + O2 << "/tmp/llvm_function_" << I.FnStart << "_" << F->getNameStr() << ".o"; + O2.flush(); + std::string Errors; + raw_fd_ostream O3(Filename.c_str(), Errors); + O3 << Buffer; + O3.close(); + } + + return Buffer; +} + +void JITDebugRegisterer::RegisterFunction(const Function *F, DebugInfo &I) { + // TODO: Support non-ELF platforms. + if (!TM.getELFWriterInfo()) + return; + + std::string Buffer = MakeELF(F, I); + + jit_code_entry *JITCodeEntry = new jit_code_entry(); + JITCodeEntry->symfile_addr = Buffer.c_str(); + JITCodeEntry->symfile_size = Buffer.size(); + + // Add a mapping from F to the entry and buffer, so we can delete this + // info later. + FnMap[F] = std::make_pair(Buffer, JITCodeEntry); + + // Acquire the lock and do the registration. + { + MutexGuard locked(JITDebugLock); + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + + // Insert this entry at the head of the list. + JITCodeEntry->prev_entry = NULL; + jit_code_entry *NextEntry = __jit_debug_descriptor.first_entry; + JITCodeEntry->next_entry = NextEntry; + if (NextEntry != NULL) { + NextEntry->prev_entry = JITCodeEntry; + } + __jit_debug_descriptor.first_entry = JITCodeEntry; + __jit_debug_descriptor.relevant_entry = JITCodeEntry; + __jit_debug_register_code(); + } +} + +void JITDebugRegisterer::UnregisterFunctionInternal( + RegisteredFunctionsMap::iterator I) { + jit_code_entry *JITCodeEntry = I->second.second; + + // Acquire the lock and do the unregistration. + { + MutexGuard locked(JITDebugLock); + __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN; + + // Remove the jit_code_entry from the linked list. + jit_code_entry *PrevEntry = JITCodeEntry->prev_entry; + jit_code_entry *NextEntry = JITCodeEntry->next_entry; + if (NextEntry) { + NextEntry->prev_entry = PrevEntry; + } + if (PrevEntry) { + PrevEntry->next_entry = NextEntry; + } else { + assert(__jit_debug_descriptor.first_entry == JITCodeEntry); + __jit_debug_descriptor.first_entry = NextEntry; + } + + // Tell GDB which entry we removed, and unregister the code. + __jit_debug_descriptor.relevant_entry = JITCodeEntry; + __jit_debug_register_code(); + } + + // Free the ELF file in memory. + std::string &Buffer = I->second.first; + Buffer.clear(); +} + +void JITDebugRegisterer::UnregisterFunction(const Function *F) { + // TODO: Support non-ELF platforms. + if (!TM.getELFWriterInfo()) + return; + + RegisteredFunctionsMap::iterator I = FnMap.find(F); + if (I == FnMap.end()) return; + UnregisterFunctionInternal(I); + FnMap.erase(I); +} + +} // end namespace llvm diff --git a/lib/ExecutionEngine/JIT/JITDebugRegisterer.h b/lib/ExecutionEngine/JIT/JITDebugRegisterer.h new file mode 100644 index 00000000000..832580daa0a --- /dev/null +++ b/lib/ExecutionEngine/JIT/JITDebugRegisterer.h @@ -0,0 +1,116 @@ +//===-- JITDebugRegisterer.h - Register debug symbols for JIT -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a JITDebugRegisterer object that is used by the JIT to +// register debug info with debuggers like GDB. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTION_ENGINE_JIT_DEBUGREGISTERER_H +#define LLVM_EXECUTION_ENGINE_JIT_DEBUGREGISTERER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/DataTypes.h" +#include + +// This must be kept in sync with gdb/gdb/jit.h . +extern "C" { + + typedef enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN + } jit_actions_t; + + struct jit_code_entry { + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const char *symfile_addr; + uint64_t symfile_size; + }; + + struct jit_descriptor { + uint32_t version; + // This should be jit_actions_t, but we want to be specific about the + // bit-width. + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; + }; + +} + +namespace llvm { + +struct ELFSection; +class Function; +class TargetMachine; + + +/// This class encapsulates information we want to send to the debugger. +/// +struct DebugInfo { + uint8_t *FnStart; + uint8_t *FnEnd; + uint8_t *EhStart; + uint8_t *EhEnd; + + DebugInfo() : FnStart(0), FnEnd(0), EhStart(0), EhEnd(0) {} +}; + +typedef DenseMap< const Function*, std::pair > + RegisteredFunctionsMap; + +/// This class registers debug info for JITed code with an attached debugger. +/// Without proper debug info, GDB can't do things like source level debugging +/// or even produce a proper stack trace on linux-x86_64. To use this class, +/// whenever a function is JITed, create a DebugInfo struct and pass it to the +/// RegisterFunction method. The method will then do whatever is necessary to +/// inform the debugger about the JITed function. +class JITDebugRegisterer { + + TargetMachine &TM; + + /// FnMap - A map of functions that have been registered to the associated + /// temporary files. Used for cleanup. + RegisteredFunctionsMap FnMap; + + /// MakeELF - Builds the ELF file in memory and returns a std::string that + /// contains the ELF. + std::string MakeELF(const Function *F, DebugInfo &I); + +public: + JITDebugRegisterer(TargetMachine &tm); + + /// ~JITDebugRegisterer - Unregisters all code and frees symbol files. + /// + ~JITDebugRegisterer(); + + /// RegisterFunction - Register debug info for the given function with an + /// attached debugger. Clients must call UnregisterFunction on all + /// registered functions before deleting them to free the associated symbol + /// file and unregister it from the debugger. + void RegisterFunction(const Function *F, DebugInfo &I); + + /// UnregisterFunction - Unregister the debug info for the given function + /// from the debugger and free associated memory. + void UnregisterFunction(const Function *F); + +private: + /// UnregisterFunctionInternal - Unregister the debug info for the given + /// function from the debugger and delete any temporary files. The private + /// version of this method does not remove the function from FnMap so that it + /// can be called while iterating over FnMap. + void UnregisterFunctionInternal(RegisteredFunctionsMap::iterator I); + +}; + +} // end namespace llvm + +#endif // LLVM_EXECUTION_ENGINE_JIT_DEBUGREGISTERER_H diff --git a/lib/ExecutionEngine/JIT/JITDwarfEmitter.cpp b/lib/ExecutionEngine/JIT/JITDwarfEmitter.cpp index b902636e363..d9651f9c3b3 100644 --- a/lib/ExecutionEngine/JIT/JITDwarfEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITDwarfEmitter.cpp @@ -36,7 +36,8 @@ JITDwarfEmitter::JITDwarfEmitter(JIT& theJit) : Jit(theJit) {} unsigned char* JITDwarfEmitter::EmitDwarfTable(MachineFunction& F, JITCodeEmitter& jce, unsigned char* StartFunction, - unsigned char* EndFunction) { + unsigned char* EndFunction, + unsigned char* &EHFramePtr) { const TargetMachine& TM = F.getTarget(); TD = TM.getTargetData(); stackGrowthDirection = TM.getFrameInfo()->getStackGrowthDirection(); @@ -47,7 +48,6 @@ unsigned char* JITDwarfEmitter::EmitDwarfTable(MachineFunction& F, EndFunction); unsigned char* Result = 0; - unsigned char* EHFramePtr = 0; const std::vector Personalities = MMI->getPersonalities(); EHFramePtr = EmitCommonEHFrame(Personalities[MMI->getPersonalityIndex()]); diff --git a/lib/ExecutionEngine/JIT/JITDwarfEmitter.h b/lib/ExecutionEngine/JIT/JITDwarfEmitter.h index f82c7007474..e627550d6d0 100644 --- a/lib/ExecutionEngine/JIT/JITDwarfEmitter.h +++ b/lib/ExecutionEngine/JIT/JITDwarfEmitter.h @@ -67,7 +67,8 @@ public: unsigned char* EmitDwarfTable(MachineFunction& F, JITCodeEmitter& JCE, unsigned char* StartFunction, - unsigned char* EndFunction); + unsigned char* EndFunction, + unsigned char* &EHFramePtr); unsigned GetDwarfTableSizeInBytes(MachineFunction& F, diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index e1799600e53..590846bfded 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -14,7 +14,9 @@ #define DEBUG_TYPE "jit" #include "JIT.h" +#include "JITDebugRegisterer.h" #include "JITDwarfEmitter.h" +#include "llvm/ADT/OwningPtr.h" #include "llvm/Constants.h" #include "llvm/Module.h" #include "llvm/DerivedTypes.h" @@ -464,9 +466,12 @@ namespace { /// Resolver - This contains info about the currently resolved functions. JITResolver Resolver; - + /// DE - The dwarf emitter for the jit. - JITDwarfEmitter *DE; + OwningPtr DE; + + /// DR - The debug registerer for the jit. + OwningPtr DR; /// LabelLocations - This vector is a mapping from Label ID's to their /// address. @@ -504,7 +509,7 @@ namespace { DebugLocTuple PrevDLT; public: - JITEmitter(JIT &jit, JITMemoryManager *JMM) + JITEmitter(JIT &jit, JITMemoryManager *JMM, TargetMachine &TM) : SizeEstimate(0), Resolver(jit), MMI(0), CurFn(0) { MemMgr = JMM ? JMM : JITMemoryManager::CreateDefaultMemManager(); if (jit.getJITInfo().needsGOT()) { @@ -512,11 +517,15 @@ namespace { DEBUG(errs() << "JIT is managing a GOT\n"); } - if (DwarfExceptionHandling) DE = new JITDwarfEmitter(jit); + if (DwarfExceptionHandling || JITEmitDebugInfo) { + DE.reset(new JITDwarfEmitter(jit)); + } + if (JITEmitDebugInfo) { + DR.reset(new JITDebugRegisterer(TM)); + } } ~JITEmitter() { delete MemMgr; - if (DwarfExceptionHandling) delete DE; } /// classof - Methods for support type inquiry through isa, cast, and @@ -604,7 +613,7 @@ namespace { virtual void setModuleInfo(MachineModuleInfo* Info) { MMI = Info; - if (DwarfExceptionHandling) DE->setModuleInfo(Info); + if (DE.get()) DE->setModuleInfo(Info); } void setMemoryExecutable() { @@ -1124,12 +1133,12 @@ bool JITEmitter::finishFunction(MachineFunction &F) { } ); - if (DwarfExceptionHandling) { + if (DwarfExceptionHandling || JITEmitDebugInfo) { uintptr_t ActualSize = 0; SavedBufferBegin = BufferBegin; SavedBufferEnd = BufferEnd; SavedCurBufferPtr = CurBufferPtr; - + if (MemMgr->NeedsExactSize()) { ActualSize = DE->GetDwarfTableSizeInBytes(F, *this, FnStart, FnEnd); } @@ -1137,14 +1146,28 @@ bool JITEmitter::finishFunction(MachineFunction &F) { BufferBegin = CurBufferPtr = MemMgr->startExceptionTable(F.getFunction(), ActualSize); BufferEnd = BufferBegin+ActualSize; - uint8_t* FrameRegister = DE->EmitDwarfTable(F, *this, FnStart, FnEnd); + uint8_t *EhStart; + uint8_t *FrameRegister = DE->EmitDwarfTable(F, *this, FnStart, FnEnd, + EhStart); MemMgr->endExceptionTable(F.getFunction(), BufferBegin, CurBufferPtr, FrameRegister); + uint8_t *EhEnd = CurBufferPtr; BufferBegin = SavedBufferBegin; BufferEnd = SavedBufferEnd; CurBufferPtr = SavedCurBufferPtr; - TheJIT->RegisterTable(FrameRegister); + if (DwarfExceptionHandling) { + TheJIT->RegisterTable(FrameRegister); + } + + if (JITEmitDebugInfo) { + DebugInfo I; + I.FnStart = FnStart; + I.FnEnd = FnEnd; + I.EhStart = EhStart; + I.EhEnd = EhEnd; + DR->RegisterFunction(F.getFunction(), I); + } } if (MMI) @@ -1168,6 +1191,13 @@ void JITEmitter::retryWithMoreMemory(MachineFunction &F) { void JITEmitter::deallocateMemForFunction(const Function *F) { MemMgr->deallocateMemForFunction(F); + // TODO: Do we need to unregister exception handling information from libgcc + // here? + + if (JITEmitDebugInfo) { + DR->UnregisterFunction(F); + } + // If the function did not reference any stubs, return. if (CurFnStubUses.find(F) == CurFnStubUses.end()) return; @@ -1390,8 +1420,9 @@ uintptr_t JITEmitter::getJumpTableEntryAddress(unsigned Index) const { // Public interface to this file //===----------------------------------------------------------------------===// -JITCodeEmitter *JIT::createEmitter(JIT &jit, JITMemoryManager *JMM) { - return new JITEmitter(jit, JMM); +JITCodeEmitter *JIT::createEmitter(JIT &jit, JITMemoryManager *JMM, + TargetMachine &tm) { + return new JITEmitter(jit, JMM, tm); } // getPointerToNamedFunction - This function is used as a global wrapper to diff --git a/lib/Target/TargetMachine.cpp b/lib/Target/TargetMachine.cpp index fdf157a1e61..fec59b5e2b5 100644 --- a/lib/Target/TargetMachine.cpp +++ b/lib/Target/TargetMachine.cpp @@ -35,6 +35,8 @@ namespace llvm { bool NoZerosInBSS; bool DwarfExceptionHandling; bool SjLjExceptionHandling; + bool JITEmitDebugInfo; + bool JITEmitDebugInfoToDisk; bool UnwindTablesMandatory; Reloc::Model RelocationModel; CodeModel::Model CMModel; @@ -114,6 +116,24 @@ EnableSjLjExceptionHandling("enable-sjlj-eh", cl::desc("Emit SJLJ exception handling (default if target supports)"), cl::location(SjLjExceptionHandling), cl::init(false)); +// In debug builds, make this default to true. +#ifdef NDEBUG +#define EMIT_DEBUG false +#else +#define EMIT_DEBUG true +#endif +static cl::opt +EmitJitDebugInfo("jit-emit-debug", + cl::desc("Emit debug information to debugger"), + cl::location(JITEmitDebugInfo), + cl::init(EMIT_DEBUG)); +#undef EMIT_DEBUG +static cl::opt +EmitJitDebugInfoToDisk("jit-emit-debug-to-disk", + cl::Hidden, + cl::desc("Emit debug info objfiles to disk"), + cl::location(JITEmitDebugInfoToDisk), + cl::init(false)); static cl::opt EnableUnwindTables("unwind-tables", cl::desc("Generate unwinding tables for all functions"), @@ -243,4 +263,3 @@ namespace llvm { return !UnsafeFPMath && HonorSignDependentRoundingFPMathOption; } } -