DebugIR.cpp revision 13ace6664fad8b4d0277d16690674f4e1f176642
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" 44using namespace llvm; 45 46namespace { 47 48/// Returns true if Node's name contains the string "llvm.dbg" 49bool 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 54bool 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). 65class 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 77public: 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 86private: 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 177class DebugIR : public ModulePass { 178 std::string Postfix; 179 std::string Filename; 180 DebugInfoFinder Finder; 181 182public: 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 191private: 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 242char DebugIR::ID = 0; 243INITIALIZE_PASS(DebugIR, "debug-ir", "Enable debugging IR", false, false) 244 ModulePass *llvm::createDebugIRPass(StringRef FilenamePostfix) { 245 return new DebugIR(FilenamePostfix); 246} 247