Daniel Malea | 13ace66 | 2013-05-08 20:44:14 +0000 | [diff] [blame] | 1 | //===--- DebugIR.cpp - Transform debug metadata to allow debugging IR -----===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // A Module transform pass that emits a succinct version of the IR and replaces |
| 11 | // the source file metadata to allow debuggers to step through the IR. |
| 12 | // |
| 13 | // The location where the IR file is emitted is the same as the directory |
| 14 | // operand of the !llvm.dbg.cu metadata node present in the input module. The |
| 15 | // file name is constructed from the original file name by stripping the |
| 16 | // extension and replacing it with "-debug.ll" or the Postfix string specified |
| 17 | // at construction. |
| 18 | // |
| 19 | // FIXME: instead of replacing debug metadata, additional metadata should be |
| 20 | // used to point capable debuggers to the IR file without destroying the |
| 21 | // mapping to the original source file. |
| 22 | // |
| 23 | // FIXME: this pass should not depend on the existance of debug metadata in |
| 24 | // the module as it does now. Instead, it should use DIBuilder to create the |
| 25 | // required metadata. |
| 26 | // |
| 27 | //===----------------------------------------------------------------------===// |
| 28 | |
| 29 | #include <string> |
| 30 | |
| 31 | #include "llvm/ADT/ArrayRef.h" |
| 32 | #include "llvm/ADT/SmallSet.h" |
| 33 | #include "llvm/DebugInfo.h" |
| 34 | #include "llvm/DIBuilder.h" |
| 35 | #include "llvm/IR/AsmWriter.h" |
| 36 | #include "llvm/IR/Instruction.h" |
| 37 | #include "llvm/IR/IntrinsicInst.h" |
| 38 | #include "llvm/IR/Module.h" |
| 39 | #include "llvm/Pass.h" |
| 40 | #include "llvm/Transforms/Instrumentation.h" |
| 41 | #include "llvm/Support/Debug.h" |
| 42 | #include "llvm/Support/ToolOutputFile.h" |
| 43 | #include "llvm/Support/raw_ostream.h" |
| 44 | using namespace llvm; |
| 45 | |
| 46 | namespace { |
| 47 | |
| 48 | /// Returns true if Node's name contains the string "llvm.dbg" |
| 49 | bool isDebugNamedMetadata(const NamedMDNode *Node) { |
| 50 | return Node->getName().str().find("llvm.dbg") != std::string::npos; |
| 51 | } |
| 52 | |
| 53 | /// Returns true if Inst is a call to llvm.dbg.value or llvm.dbg.declare |
| 54 | bool isDebugIntrinsic(const IntrinsicInst *Inst) { |
| 55 | Intrinsic::ID id = Inst->getIntrinsicID(); |
| 56 | return id == Intrinsic::dbg_value || id == Intrinsic::dbg_declare; |
| 57 | } |
| 58 | |
| 59 | /// An AssemblyWriter which generates a succinct representation of the module |
| 60 | /// (without debug intrinsics or metadata) suitable for debugging. As IR |
| 61 | /// instructions are printed, !dbg metadata nodes are added (or updated) |
| 62 | /// to point to the corresponding line in the generated IR file instead |
| 63 | /// of the original source file. The input module must have been constructed |
| 64 | /// with debug metadata (i.e. clang -g). |
| 65 | class IRDebugInfoHelper : public llvm::AssemblyWriter { |
| 66 | /// Directory of debug metadata |
| 67 | const DebugInfoFinder &Finder; |
| 68 | |
| 69 | /// Flags to control the verbosity of the generated IR file |
| 70 | bool hideDebugIntrinsics; |
| 71 | bool hideDebugMetadata; |
| 72 | |
| 73 | /// Set to track metadata nodes to be printed (used only when |
| 74 | /// printDebugMetadata == false) |
| 75 | SmallSet<const MDNode *, 4> NonDebugNodes; |
| 76 | |
| 77 | public: |
| 78 | IRDebugInfoHelper( |
| 79 | formatted_raw_ostream &o, const Module *M, |
| 80 | AssemblyAnnotationWriter *AAW, const DebugInfoFinder &Finder, |
| 81 | bool hideDebugIntrinsics = true, bool hideDebugMetadata = true) |
| 82 | : AssemblyWriter(o, M, AAW), Finder(Finder), |
| 83 | hideDebugIntrinsics(hideDebugIntrinsics), |
| 84 | hideDebugMetadata(hideDebugMetadata) {} |
| 85 | |
| 86 | private: |
| 87 | virtual void printInstruction(const Instruction &I) { |
| 88 | DebugLoc Loc(I.getDebugLoc()); |
| 89 | |
| 90 | if (hideDebugMetadata) |
| 91 | removeDebugMetadata(const_cast<Instruction &>(I)); |
| 92 | |
| 93 | AssemblyWriter::printInstruction(I); |
| 94 | Out.flush(); |
| 95 | // Adjust line number by 1 because we have not yet printed the \n |
| 96 | unsigned Line = Out.getLine() + 1; |
| 97 | |
| 98 | DebugLoc NewLoc; |
| 99 | if (!Loc.isUnknown()) |
| 100 | // I had a previous debug location: re-use the DebugLoc |
| 101 | NewLoc = DebugLoc::get(Line, /* FIXME: support columns */ 0, |
| 102 | Loc.getScope(I.getContext()), |
| 103 | Loc.getInlinedAt(I.getContext())); |
| 104 | else if (MDNode *scope = findFunctionMD(I.getParent()->getParent())) |
| 105 | // I had no previous debug location, but M has some debug information |
| 106 | NewLoc = DebugLoc::get(Line, 0, scope, /*FIXME: inlined instructions*/ 0); |
| 107 | else |
| 108 | // Neither I nor M has any debug information -- nothing to do here. |
| 109 | // FIXME: support debugging of undecorated IR (generated by clang without |
| 110 | // the -g option) |
| 111 | return; |
| 112 | |
| 113 | if (hideDebugMetadata) |
| 114 | saveNonDebugMetadata(I); |
| 115 | |
| 116 | addDebugLocation(const_cast<Instruction &>(I), NewLoc); |
| 117 | } |
| 118 | |
| 119 | virtual void printInstructionLine(const Instruction &I) { |
| 120 | if (hideDebugIntrinsics) |
| 121 | if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I)) |
| 122 | if (isDebugIntrinsic(II)) |
| 123 | return; |
| 124 | AssemblyWriter::printInstructionLine(I); |
| 125 | } |
| 126 | |
| 127 | virtual void writeMDNode(unsigned Slot, const MDNode *Node) { |
| 128 | if (hideDebugMetadata == false || isDebugMetadata(Node) == false) |
| 129 | AssemblyWriter::writeMDNode(Slot, Node); |
| 130 | } |
| 131 | |
| 132 | virtual void printNamedMDNode(const NamedMDNode *NMD) { |
| 133 | if (hideDebugMetadata == false || isDebugNamedMetadata(NMD) == false) |
| 134 | AssemblyWriter::printNamedMDNode(NMD); |
| 135 | } |
| 136 | |
| 137 | /// Returns the MDNode that corresponds with F |
| 138 | MDNode *findFunctionMD(const Function *F) { |
| 139 | for (DebugInfoFinder::iterator i = Finder.subprogram_begin(), |
| 140 | e = Finder.subprogram_end(); |
| 141 | i != e; ++i) { |
| 142 | DISubprogram S(*i); |
| 143 | if (S.getFunction() == F) |
| 144 | return *i; |
| 145 | } |
| 146 | // cannot find F -- likely means there is no debug information |
| 147 | return 0; |
| 148 | } |
| 149 | |
| 150 | /// Saves all non-debug metadata attached to I |
| 151 | void saveNonDebugMetadata(const Instruction &I) { |
| 152 | typedef SmallVector<std::pair<unsigned, MDNode *>, 4> MDNodeVector; |
| 153 | MDNodeVector Others; |
| 154 | I.getAllMetadataOtherThanDebugLoc(Others); |
| 155 | for (MDNodeVector::iterator i = Others.begin(), e = Others.end(); i != e; |
| 156 | ++i) |
| 157 | NonDebugNodes.insert(i->second); |
| 158 | } |
| 159 | |
| 160 | /// Returns true if Node was not saved as non-debug metadata with |
| 161 | /// saveNonDebugMetadata(), false otherwise. |
| 162 | bool isDebugMetadata(const MDNode *Node) { |
| 163 | return NonDebugNodes.count(Node) == 0; |
| 164 | } |
| 165 | |
| 166 | void removeDebugMetadata(Instruction &I) { |
| 167 | if (I.getMetadata(LLVMContext::MD_dbg)) |
| 168 | I.setMetadata(LLVMContext::MD_dbg, 0); |
| 169 | } |
| 170 | |
| 171 | void addDebugLocation(Instruction &I, DebugLoc Loc) { |
| 172 | MDNode *MD = Loc.getAsMDNode(I.getContext()); |
| 173 | I.setMetadata(LLVMContext::MD_dbg, MD); |
| 174 | } |
| 175 | }; |
| 176 | |
| 177 | class DebugIR : public ModulePass { |
| 178 | std::string Postfix; |
| 179 | std::string Filename; |
| 180 | DebugInfoFinder Finder; |
| 181 | |
| 182 | public: |
| 183 | static char ID; |
| 184 | |
| 185 | DebugIR() : ModulePass(ID), Postfix("-debug.ll") {} |
| 186 | |
| 187 | /// Customize the postfix string used to replace the extension of the |
| 188 | /// original filename that appears in the !llvm.dbg.cu metadata node. |
| 189 | DebugIR(StringRef postfix) : ModulePass(ID), Postfix(postfix) {} |
| 190 | |
| 191 | private: |
| 192 | // Modify the filename embedded in the Compilation-Unit debug information of M |
| 193 | bool replaceFilename(Module &M) { |
| 194 | bool changed = false; |
| 195 | |
| 196 | // Sanity check -- if llvm.dbg.cu node exists, the DebugInfoFinder |
| 197 | // better have found at least one CU! |
| 198 | if (M.getNamedMetadata("llvm.dbg.cu")) |
| 199 | assert(Finder.compile_unit_count() > 0 && |
| 200 | "Found no compile units but llvm.dbg.cu node exists"); |
| 201 | |
| 202 | for (DebugInfoFinder::iterator i = Finder.compile_unit_begin(), |
| 203 | e = Finder.compile_unit_end(); |
| 204 | i != e; ++i) { |
| 205 | DICompileUnit CU(*i); |
| 206 | Filename = CU.getFilename(); |
| 207 | |
| 208 | // Replace extension with postfix |
| 209 | size_t dot = Filename.find_last_of("."); |
| 210 | if (dot != std::string::npos) |
| 211 | Filename.erase(dot); |
| 212 | Filename += Postfix; |
| 213 | |
| 214 | CU.setFilename(Filename, M.getContext()); |
| 215 | changed = true; |
| 216 | } |
| 217 | return changed; |
| 218 | } |
| 219 | |
| 220 | void writeAndUpdateDebugIRFile(Module *M) { |
| 221 | std::string error; |
| 222 | tool_output_file OutFile(Filename.c_str(), error); |
| 223 | OutFile.keep(); |
| 224 | formatted_raw_ostream OS; |
| 225 | OS.setStream(OutFile.os(), false); |
| 226 | |
| 227 | IRDebugInfoHelper W(OS, M, 0, Finder); |
| 228 | W.printModule(M); |
| 229 | } |
| 230 | |
| 231 | bool runOnModule(Module &M) { |
| 232 | Finder.processModule(M); |
| 233 | bool changed = replaceFilename(M); |
| 234 | if (changed) |
| 235 | writeAndUpdateDebugIRFile(&M); |
| 236 | return changed; |
| 237 | } |
| 238 | }; |
| 239 | |
| 240 | } // anonymous namespace |
| 241 | |
| 242 | char DebugIR::ID = 0; |
| 243 | INITIALIZE_PASS(DebugIR, "debug-ir", "Enable debugging IR", false, false) |
| 244 | ModulePass *llvm::createDebugIRPass(StringRef FilenamePostfix) { |
| 245 | return new DebugIR(FilenamePostfix); |
| 246 | } |