diff options
author | Stephen Hines <srhines@google.com> | 2013-06-12 13:32:42 -0700 |
---|---|---|
committer | Stephen Hines <srhines@google.com> | 2013-06-12 13:32:42 -0700 |
commit | 1878f9a7874b1ff569d745c0269f49d3daf7203d (patch) | |
tree | 19a8dbaaedf6a056c617e87596b32d3f452af137 /lib/Transforms/Instrumentation/DebugIR.cpp | |
parent | 7a57f27b857ec4b243d83d392a399f02fc196c0a (diff) | |
parent | 100fbdd06be7590b23c4707a98cd605bdb519498 (diff) | |
download | external_llvm-1878f9a7874b1ff569d745c0269f49d3daf7203d.zip external_llvm-1878f9a7874b1ff569d745c0269f49d3daf7203d.tar.gz external_llvm-1878f9a7874b1ff569d745c0269f49d3daf7203d.tar.bz2 |
Merge commit '100fbdd06be7590b23c4707a98cd605bdb519498' into merge_20130612
Diffstat (limited to 'lib/Transforms/Instrumentation/DebugIR.cpp')
-rw-r--r-- | lib/Transforms/Instrumentation/DebugIR.cpp | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/lib/Transforms/Instrumentation/DebugIR.cpp b/lib/Transforms/Instrumentation/DebugIR.cpp new file mode 100644 index 0000000..020804f --- /dev/null +++ b/lib/Transforms/Instrumentation/DebugIR.cpp @@ -0,0 +1,310 @@ +//===--- 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 <string> + +#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<const Value *, unsigned int> Lines; + typedef ValueMap<const Value *, unsigned int>::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<DebugIntrinsicsRemover> { + 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<DebugMetadataRemover> { +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<LineNumberReplacer> { + /// 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<Instruction &>(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); +} |