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