SerializedDiagnosticPrinter.cpp revision b05d6c0ee5d2e4e7a75eabafa2b8e84cc7e1ab8c
1//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===//
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#include <vector>
11#include "llvm/Bitcode/BitstreamWriter.h"
12#include "llvm/Support/raw_ostream.h"
13#include "llvm/ADT/StringRef.h"
14#include "llvm/ADT/SmallString.h"
15#include "llvm/ADT/DenseSet.h"
16#include "clang/Basic/SourceManager.h"
17#include "clang/Basic/FileManager.h"
18#include "clang/Basic/Diagnostic.h"
19#include "clang/Basic/Version.h"
20#include "clang/Frontend/SerializedDiagnosticPrinter.h"
21
22using namespace clang;
23
24namespace {
25
26/// \brief A utility class for entering and exiting bitstream blocks.
27class BlockEnterExit {
28  llvm::BitstreamWriter &Stream;
29public:
30  BlockEnterExit(llvm::BitstreamWriter &stream, unsigned blockID,
31                 unsigned codelen = 3)
32    : Stream(stream) {
33      Stream.EnterSubblock(blockID, codelen);
34  }
35  ~BlockEnterExit() {
36    Stream.ExitBlock();
37  }
38};
39
40class AbbreviationMap {
41  llvm::DenseMap<unsigned, unsigned> Abbrevs;
42public:
43  AbbreviationMap() {}
44
45  void set(unsigned recordID, unsigned abbrevID) {
46    assert(Abbrevs.find(recordID) == Abbrevs.end()
47           && "Abbreviation already set.");
48    Abbrevs[recordID] = abbrevID;
49  }
50
51  unsigned get(unsigned recordID) {
52    assert(Abbrevs.find(recordID) != Abbrevs.end() &&
53           "Abbreviation not set.");
54    return Abbrevs[recordID];
55  }
56};
57
58typedef llvm::SmallVector<uint64_t, 64> RecordData;
59typedef llvm::SmallVectorImpl<uint64_t> RecordDataImpl;
60
61class SDiagsWriter : public DiagnosticConsumer {
62public:
63  SDiagsWriter(DiagnosticsEngine &diags, llvm::raw_ostream *os)
64    : Stream(Buffer), OS(os), Diags(diags), inNonNoteDiagnostic(false)
65  {
66    EmitPreamble();
67  };
68
69  ~SDiagsWriter() {}
70
71  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
72                        const Diagnostic &Info);
73
74  void EndSourceFile();
75
76  DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
77    // It makes no sense to clone this.
78    return 0;
79  }
80
81private:
82  /// \brief Emit the preamble for the serialized diagnostics.
83  void EmitPreamble();
84
85  /// \brief Emit the BLOCKINFO block.
86  void EmitBlockInfoBlock();
87
88  /// \brief Emit the raw characters of the provided string.
89  void EmitRawStringContents(StringRef str);
90
91  /// \brief Emit the block containing categories and file names.
92  void EmitCategoriesAndFileNames();
93
94  /// \brief The version of the diagnostics file.
95  enum { Version = 1 };
96
97  /// \brief The byte buffer for the serialized content.
98  std::vector<unsigned char> Buffer;
99
100  /// \brief The BitStreamWriter for the serialized diagnostics.
101  llvm::BitstreamWriter Stream;
102
103  /// \brief The name of the diagnostics file.
104  llvm::OwningPtr<llvm::raw_ostream> OS;
105
106  /// \brief The DiagnosticsEngine tied to all diagnostic locations.
107  DiagnosticsEngine &Diags;
108
109  /// \brief The set of constructed record abbreviations.
110  AbbreviationMap Abbrevs;
111
112  /// \brief A utility buffer for constructing record content.
113  RecordData Record;
114
115  /// \brief A text buffer for rendering diagnostic text.
116  llvm::SmallString<256> diagBuf;
117
118  /// \brief The collection of diagnostic categories used.
119  llvm::DenseSet<unsigned> Categories;
120
121  /// \brief The collection of files used.
122  llvm::DenseSet<FileID> Files;
123
124  typedef llvm::DenseMap<const void *, std::pair<unsigned, llvm::StringRef> >
125          DiagFlagsTy;
126
127  /// \brief Map for uniquing strings.
128  DiagFlagsTy DiagFlags;
129
130  /// \brief Flag indicating whether or not we are in the process of
131  /// emitting a non-note diagnostic.
132  bool inNonNoteDiagnostic;
133
134  enum BlockIDs {
135    /// \brief The DIAG block, which acts as a container around a diagnostic.
136    BLOCK_DIAG = llvm::bitc::FIRST_APPLICATION_BLOCKID,
137    /// \brief The STRINGS block, which contains strings
138    /// from multiple diagnostics.
139    BLOCK_STRINGS
140  };
141
142  enum RecordIDs {
143    RECORD_DIAG = 1,
144    RECORD_DIAG_FLAG,
145    RECORD_CATEGORY,
146    RECORD_FILENAME
147  };
148};
149} // end anonymous namespace
150
151namespace clang {
152namespace serialized_diags {
153DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) {
154  return new SDiagsWriter(Diags, OS);
155}
156} // end namespace serialized_diags
157} // end namespace clang
158
159//===----------------------------------------------------------------------===//
160// Serialization methods.
161//===----------------------------------------------------------------------===//
162
163/// \brief Emits a block ID in the BLOCKINFO block.
164static void EmitBlockID(unsigned ID, const char *Name,
165                        llvm::BitstreamWriter &Stream,
166                        RecordDataImpl &Record) {
167  Record.clear();
168  Record.push_back(ID);
169  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);
170
171  // Emit the block name if present.
172  if (Name == 0 || Name[0] == 0)
173    return;
174
175  Record.clear();
176
177  while (*Name)
178    Record.push_back(*Name++);
179
180  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
181}
182
183/// \brief Emits a record ID in the BLOCKINFO block.
184static void EmitRecordID(unsigned ID, const char *Name,
185                         llvm::BitstreamWriter &Stream,
186                         RecordDataImpl &Record){
187  Record.clear();
188  Record.push_back(ID);
189
190  while (*Name)
191    Record.push_back(*Name++);
192
193  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
194}
195
196/// \brief Emits the preamble of the diagnostics file.
197void SDiagsWriter::EmitPreamble() {
198 // EmitRawStringContents("CLANG_DIAGS");
199 // Stream.Emit(Version, 32);
200
201  // Emit the file header.
202  Stream.Emit((unsigned)'D', 8);
203  Stream.Emit((unsigned) Version, 32 - 8);
204
205  EmitBlockInfoBlock();
206}
207
208void SDiagsWriter::EmitBlockInfoBlock() {
209  Stream.EnterBlockInfoBlock(3);
210
211  // ==---------------------------------------------------------------------==//
212  // The subsequent records and Abbrevs are for the "Diagnostic" block.
213  // ==---------------------------------------------------------------------==//
214
215  EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record);
216  EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record);
217
218  // Emit Abbrevs.
219  using namespace llvm;
220
221  // Emit abbreviation for RECORD_DIAG.
222  BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
223  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));
224  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3));  // Diag level.
225  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category.
226  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
227  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
228  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.
229  Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
230
231  // ==---------------------------------------------------------------------==//
232  // The subsequent records and Abbrevs are for the "Strings" block.
233  // ==---------------------------------------------------------------------==//
234
235  EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record);
236  EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record);
237  EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record);
238  EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record);
239
240  Abbrev = new BitCodeAbbrev();
241  Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
242  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size.
243  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text.
244  Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
245                                                          Abbrev));
246
247  Abbrev = new BitCodeAbbrev();
248  Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));
249  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Size.
250  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Modifcation time.
251  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
252  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
253  Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
254                                                          Abbrev));
255
256  // Emit the abbreviation for RECORD_DIAG_FLAG.
257  Abbrev = new BitCodeAbbrev();
258  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));
259  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
260  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
261  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.
262  Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
263                                                           Abbrev));
264
265  Stream.ExitBlock();
266}
267
268void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) {
269  for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I)
270    Stream.Emit(*I, 8);
271}
272
273void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
274                                    const Diagnostic &Info) {
275
276  if (DiagLevel != DiagnosticsEngine::Note) {
277    if (inNonNoteDiagnostic) {
278      // We have encountered a non-note diagnostic.  Finish up the previous
279      // diagnostic block before starting a new one.
280      Stream.ExitBlock();
281    }
282    inNonNoteDiagnostic = true;
283  }
284
285  Stream.EnterSubblock(BLOCK_DIAG, 3);
286
287  // Emit the RECORD_DIAG record.
288  Record.clear();
289  Record.push_back(RECORD_DIAG);
290  Record.push_back(DiagLevel);
291  unsigned category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
292  Record.push_back(category);
293  Categories.insert(category);
294  if (DiagLevel == DiagnosticsEngine::Note)
295    Record.push_back(0); // No flag for notes.
296  else {
297    StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
298    if (FlagName.empty())
299      Record.push_back(0);
300    else {
301      // Here we assume that FlagName points to static data whose pointer
302      // value is fixed.
303      const void *data = FlagName.data();
304      std::pair<unsigned, StringRef> &entry = DiagFlags[data];
305      if (entry.first == 0) {
306        entry.first = DiagFlags.size();
307        entry.second = FlagName;
308      }
309      Record.push_back(entry.first);
310    }
311  }
312
313  diagBuf.clear();
314  Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
315  Record.push_back(diagBuf.str().size());
316  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());
317
318  // FIXME: emit location
319  // FIXME: emit ranges
320  // FIXME: emit fixits
321
322  if (DiagLevel == DiagnosticsEngine::Note) {
323    // Notes currently cannot have child diagnostics.  Complete the
324    // diagnostic now.
325    Stream.ExitBlock();
326  }
327}
328
329template <typename T>
330static void populateAndSort(std::vector<T> &scribble,
331                            llvm::DenseSet<T> &set) {
332  scribble.clear();
333
334  for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end();
335       it != ei; ++it)
336    scribble.push_back(*it);
337
338  // Sort 'scribble' so we always have a deterministic ordering in the
339  // serialized file.
340  std::sort(scribble.begin(), scribble.end());
341}
342
343void SDiagsWriter::EmitCategoriesAndFileNames() {
344
345  if (Categories.empty() && Files.empty())
346    return;
347
348  BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS);
349
350  // Emit the category names.
351  {
352    std::vector<unsigned> scribble;
353    populateAndSort(scribble, Categories);
354    for (std::vector<unsigned>::iterator it = scribble.begin(),
355          ei = scribble.end(); it != ei ; ++it) {
356      Record.clear();
357      Record.push_back(RECORD_CATEGORY);
358      StringRef catName = DiagnosticIDs::getCategoryNameFromID(*it);
359      Record.push_back(catName.size());
360      Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName);
361    }
362  }
363
364  // Emit the file names.
365  {
366    std::vector<FileID> scribble;
367    populateAndSort(scribble, Files);
368    for (std::vector<FileID>::iterator it = scribble.begin(),
369         ei = scribble.end(); it != ei; ++it) {
370      SourceManager &SM = Diags.getSourceManager();
371      const FileEntry *FE = SM.getFileEntryForID(*it);
372      StringRef Name = FE->getName();
373
374      Record.clear();
375      Record.push_back(FE->getSize());
376      Record.push_back(FE->getModificationTime());
377      Record.push_back(Name.size());
378      Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name);
379    }
380  }
381
382  // Emit the flag strings.
383  {
384    std::vector<StringRef> scribble;
385    scribble.resize(DiagFlags.size());
386
387    for (DiagFlagsTy::iterator it = DiagFlags.begin(), ei = DiagFlags.end();
388         it != ei; ++it) {
389      scribble[it->second.first - 1] = it->second.second;
390    }
391    for (unsigned i = 0, n = scribble.size(); i != n; ++i) {
392      Record.clear();
393      Record.push_back(RECORD_DIAG_FLAG);
394      Record.push_back(i+1);
395      StringRef FlagName = scribble[i];
396      Record.push_back(FlagName.size());
397      Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
398                                Record, FlagName);
399    }
400  }
401}
402
403void SDiagsWriter::EndSourceFile() {
404  if (inNonNoteDiagnostic) {
405    // Finish off any diagnostics we were in the process of emitting.
406    Stream.ExitBlock();
407    inNonNoteDiagnostic = false;
408  }
409
410  EmitCategoriesAndFileNames();
411
412  // Write the generated bitstream to "Out".
413  OS->write((char *)&Buffer.front(), Buffer.size());
414  OS->flush();
415
416  OS.reset(0);
417}
418
419