//===--- DebugIR.cpp - Transform debug metadata to allow debugging IR -----===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // A Module transform pass that emits a succinct version of the IR and replaces // the source file metadata to allow debuggers to step through the IR. // // The location where the IR file is emitted is the same as the directory // operand of the !llvm.dbg.cu metadata node present in the input module. The // file name is constructed from the original file name by stripping the // extension and replacing it with "-debug-ll" or the Postfix string specified // at construction. // // FIXME: instead of replacing debug metadata, additional metadata should be // used to point capable debuggers to the IR file without destroying the // mapping to the original source file. // // FIXME: this pass should not depend on the existance of debug metadata in // the module as it does now. Instead, it should use DIBuilder to create the // required metadata. // //===----------------------------------------------------------------------===// #include #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/ValueMap.h" #include "llvm/Assembly/AssemblyAnnotationWriter.h" #include "llvm/DebugInfo.h" #include "llvm/DIBuilder.h" #include "llvm/InstVisitor.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/FormattedStream.h" using namespace llvm; namespace { /// Builds a map of Value* to line numbers on which the Value appears in a /// textual representation of the IR by plugging into the AssemblyWriter by /// masquerading as an AssemblyAnnotationWriter. class ValueToLineMap : public AssemblyAnnotationWriter { ValueMap Lines; typedef ValueMap::const_iterator LineIter; public: /// Prints Module to a null buffer in order to build the map of Value pointers /// to line numbers. ValueToLineMap(Module *M) { raw_null_ostream ThrowAway; M->print(ThrowAway, this); } // This function is called after an Instruction, GlobalValue, or GlobalAlias // is printed. void printInfoComment(const Value &V, formatted_raw_ostream &Out) { Out.flush(); Lines.insert(std::make_pair(&V, Out.getLine() + 1)); } /// If V appears on a line in the textual IR representation, sets Line to the /// line number and returns true, otherwise returns false. bool getLine(const Value *V, unsigned int &Line) const { LineIter i = Lines.find(V); if (i != Lines.end()) { Line = i->second; return true; } return false; } }; /// Removes debug intrisncs like llvm.dbg.declare and llvm.dbg.value. class DebugIntrinsicsRemover : public InstVisitor { void remove(Instruction &I) { I.eraseFromParent(); } public: void visitDbgDeclareInst(DbgDeclareInst &I) { remove(I); } void visitDbgValueInst(DbgValueInst &I) { remove(I); } void visitDbgInfoIntrinsic(DbgInfoIntrinsic &I) { remove(I); } }; /// Removes debug metadata (!dbg) nodes from all instructions as well as /// metadata named "llvm.dbg.cu" in the Module. class DebugMetadataRemover : public InstVisitor { public: void visitInstruction(Instruction &I) { if (I.getMetadata(LLVMContext::MD_dbg)) I.setMetadata(LLVMContext::MD_dbg, 0); } void run(Module *M) { // Remove debug metadata attached to instructions visit(M); // Remove CU named metadata (and all children nodes) NamedMDNode *Node = M->getNamedMetadata("llvm.dbg.cu"); M->eraseNamedMetadata(Node); } }; /// Replaces line number metadata attached to Instruction nodes with new line /// numbers provided by the ValueToLineMap. class LineNumberReplacer : public InstVisitor { /// Table of line numbers const ValueToLineMap &LineTable; /// Table of cloned values const ValueToValueMapTy &VMap; /// Directory of debug metadata const DebugInfoFinder &Finder; public: LineNumberReplacer(const ValueToLineMap &VLM, const DebugInfoFinder &Finder, const ValueToValueMapTy &VMap) : LineTable(VLM), VMap(VMap), Finder(Finder) {} void visitInstruction(Instruction &I) { DebugLoc Loc(I.getDebugLoc()); unsigned Col = 0; // FIXME: support columns unsigned Line; if (!LineTable.getLine(VMap.lookup(&I), Line)) // Instruction has no line, it may have been removed (in the module that // will be passed to the debugger) so there is nothing to do here. return; DebugLoc NewLoc; if (!Loc.isUnknown()) // I had a previous debug location: re-use the DebugLoc NewLoc = DebugLoc::get(Line, Col, Loc.getScope(I.getContext()), Loc.getInlinedAt(I.getContext())); else if (MDNode *scope = findFunctionMD(I.getParent()->getParent())) // I had no previous debug location, but M has some debug information NewLoc = DebugLoc::get(Line, Col, scope, /*FIXME: inlined instructions*/ 0); else // Neither I nor M has any debug information -- nothing to do here. // FIXME: support debugging of undecorated IR (generated by clang without // the -g option) return; addDebugLocation(const_cast(I), NewLoc); } private: /// Returns the MDNode that corresponds with F MDNode *findFunctionMD(const Function *F) { for (DebugInfoFinder::iterator i = Finder.subprogram_begin(), e = Finder.subprogram_end(); i != e; ++i) { DISubprogram S(*i); if (S.getFunction() == F) return *i; } // cannot find F -- likely means there is no debug information return 0; } void addDebugLocation(Instruction &I, DebugLoc Loc) { MDNode *MD = Loc.getAsMDNode(I.getContext()); I.setMetadata(LLVMContext::MD_dbg, MD); } }; class DebugIR : public ModulePass { std::string Postfix; std::string Filename; /// Flags to control the verbosity of the generated IR file bool hideDebugIntrinsics; bool hideDebugMetadata; public: static char ID; const char *getPassName() const { return "DebugIR"; } // FIXME: figure out if we are compiling something that already exists on disk // in text IR form, in which case we can omit outputting a new IR file, or if // we're building something from memory where we actually need to emit a new // IR file for the debugger. /// Output a file with the same base name as the original, but with the /// postfix "-debug-ll" appended. DebugIR() : ModulePass(ID), Postfix("-debug-ll"), hideDebugIntrinsics(true), hideDebugMetadata(true) {} /// Customize the postfix string used to replace the extension of the /// original filename that appears in the !llvm.dbg.cu metadata node. DebugIR(StringRef postfix, bool hideDebugIntrinsics, bool hideDebugMetadata) : ModulePass(ID), Postfix(postfix), hideDebugIntrinsics(hideDebugIntrinsics), hideDebugMetadata(hideDebugMetadata) {} private: // Modify the filename embedded in the Compilation-Unit debug information of M bool replaceFilename(Module &M, const DebugInfoFinder &Finder) { bool changed = false; // Sanity check -- if llvm.dbg.cu node exists, the DebugInfoFinder // better have found at least one CU! if (M.getNamedMetadata("llvm.dbg.cu")) assert(Finder.compile_unit_count() > 0 && "Found no compile units but llvm.dbg.cu node exists"); for (DebugInfoFinder::iterator i = Finder.compile_unit_begin(), e = Finder.compile_unit_end(); i != e; ++i) { DICompileUnit CU(*i); Filename = CU.getFilename(); // Replace extension with postfix size_t dot = Filename.find_last_of("."); if (dot != std::string::npos) Filename.erase(dot); Filename += Postfix; CU.setFilename(Filename, M.getContext()); changed = true; } return changed; } /// Replace existing line number metadata with line numbers that correspond /// with the IR file that is seen by the debugger. void addLineNumberMetadata(Module *M, const ValueToLineMap &VLM, const ValueToValueMapTy &VMap, const DebugInfoFinder &Finder) { LineNumberReplacer Replacer(VLM, Finder, VMap); Replacer.visit(M); } void writeDebugBitcode(Module *M) { std::string error; tool_output_file OutFile(Filename.c_str(), error); OutFile.keep(); formatted_raw_ostream OS; OS.setStream(OutFile.os()); M->print(OS, 0); } void removeDebugIntrinsics(Module *M) { DebugIntrinsicsRemover Remover; Remover.visit(M); } void removeDebugMetadata(Module *M) { DebugMetadataRemover Remover; Remover.run(M); } void updateAndWriteDebugIRFile(Module *M, const DebugInfoFinder &Finder) { // The module we output in text form for a debugger to open is stripped of // 'extras' like debug intrinsics that end up in DWARF anyways and just // clutter the debug experience. ValueToValueMapTy VMap; Module *DebuggerM = CloneModule(M, VMap); if (hideDebugIntrinsics) removeDebugIntrinsics(DebuggerM); if (hideDebugMetadata) removeDebugMetadata(DebuggerM); // FIXME: remove all debug metadata from M once we support generating DWARF // subprogram attributes. ValueToLineMap LineTable(DebuggerM); addLineNumberMetadata(M, LineTable, VMap, Finder); writeDebugBitcode(DebuggerM); } bool runOnModule(Module &M) { // Stores existing debug info needed when creating new line number entries. DebugInfoFinder Finder; Finder.processModule(M); bool changed = replaceFilename(M, Finder); if (changed) updateAndWriteDebugIRFile(&M, Finder); return changed; } }; } // anonymous namespace char DebugIR::ID = 0; INITIALIZE_PASS(DebugIR, "debug-ir", "Enable debugging IR", false, false) ModulePass *llvm::createDebugIRPass(StringRef FilenamePostfix, bool hideDebugIntrinsics, bool hideDebugMetadata) { return new DebugIR(FilenamePostfix, hideDebugIntrinsics, hideDebugMetadata); }