SerializedDiagnosticPrinter.cpp revision 3baf63d37d0b6267885b584db1106232fc036cb9
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/Support/raw_ostream.h"
12#include "llvm/ADT/StringRef.h"
13#include "llvm/ADT/SmallString.h"
14#include "llvm/ADT/DenseSet.h"
15#include "clang/Basic/SourceManager.h"
16#include "clang/Basic/FileManager.h"
17#include "clang/Basic/Diagnostic.h"
18#include "clang/Basic/Version.h"
19#include "clang/Frontend/SerializedDiagnosticPrinter.h"
20
21using namespace clang;
22using namespace clang::serialized_diags;
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 Emit a record for a CharSourceRange.
95  void EmitCharSourceRange(CharSourceRange R);
96
97  /// \brief Emit the string information for the category for a diagnostic.
98  unsigned getEmitCategory(unsigned DiagID);
99
100  /// \brief Emit the string information for diagnostic flags.
101  unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,
102                                 const Diagnostic &Info);
103
104  /// \brief The version of the diagnostics file.
105  enum { Version = 1 };
106
107  /// \brief The byte buffer for the serialized content.
108  std::vector<unsigned char> Buffer;
109
110  /// \brief The BitStreamWriter for the serialized diagnostics.
111  llvm::BitstreamWriter Stream;
112
113  /// \brief The name of the diagnostics file.
114  llvm::OwningPtr<llvm::raw_ostream> OS;
115
116  /// \brief The DiagnosticsEngine tied to all diagnostic locations.
117  DiagnosticsEngine &Diags;
118
119  /// \brief The set of constructed record abbreviations.
120  AbbreviationMap Abbrevs;
121
122  /// \brief A utility buffer for constructing record content.
123  RecordData Record;
124
125  /// \brief A text buffer for rendering diagnostic text.
126  llvm::SmallString<256> diagBuf;
127
128  /// \brief The collection of diagnostic categories used.
129  llvm::DenseSet<unsigned> Categories;
130
131  /// \brief The collection of files used.
132  llvm::DenseSet<FileID> Files;
133
134  typedef llvm::DenseMap<const void *, std::pair<unsigned, llvm::StringRef> >
135          DiagFlagsTy;
136
137  /// \brief Map for uniquing strings.
138  DiagFlagsTy DiagFlags;
139
140  /// \brief Flag indicating whether or not we are in the process of
141  /// emitting a non-note diagnostic.
142  bool inNonNoteDiagnostic;
143};
144} // end anonymous namespace
145
146namespace clang {
147namespace serialized_diags {
148DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) {
149  return new SDiagsWriter(Diags, OS);
150}
151} // end namespace serialized_diags
152} // end namespace clang
153
154//===----------------------------------------------------------------------===//
155// Serialization methods.
156//===----------------------------------------------------------------------===//
157
158/// \brief Emits a block ID in the BLOCKINFO block.
159static void EmitBlockID(unsigned ID, const char *Name,
160                        llvm::BitstreamWriter &Stream,
161                        RecordDataImpl &Record) {
162  Record.clear();
163  Record.push_back(ID);
164  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);
165
166  // Emit the block name if present.
167  if (Name == 0 || Name[0] == 0)
168    return;
169
170  Record.clear();
171
172  while (*Name)
173    Record.push_back(*Name++);
174
175  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
176}
177
178/// \brief Emits a record ID in the BLOCKINFO block.
179static void EmitRecordID(unsigned ID, const char *Name,
180                         llvm::BitstreamWriter &Stream,
181                         RecordDataImpl &Record){
182  Record.clear();
183  Record.push_back(ID);
184
185  while (*Name)
186    Record.push_back(*Name++);
187
188  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
189}
190
191static void AddLocToRecord(SourceManager &SM,
192                           SourceLocation Loc,
193                           RecordDataImpl &Record) {
194  if (Loc.isInvalid()) {
195    // Emit a "sentinel" location.
196    Record.push_back(~(unsigned)0);  // Line.
197    Record.push_back(~(unsigned)0);  // Column.
198    Record.push_back(~(unsigned)0);  // Offset.
199    return;
200  }
201
202  Loc = SM.getSpellingLoc(Loc);
203  Record.push_back(SM.getSpellingLineNumber(Loc));
204  Record.push_back(SM.getSpellingColumnNumber(Loc));
205
206  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
207  FileID FID = LocInfo.first;
208  unsigned FileOffset = LocInfo.second;
209  Record.push_back(FileOffset);
210}
211
212void SDiagsWriter::EmitCharSourceRange(CharSourceRange R) {
213  Record.clear();
214  Record.push_back(RECORD_SOURCE_RANGE);
215  AddLocToRecord(Diags.getSourceManager(), R.getBegin(), Record);
216  AddLocToRecord(Diags.getSourceManager(), R.getEnd(), Record);
217  Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_SOURCE_RANGE), Record);
218}
219
220/// \brief Emits the preamble of the diagnostics file.
221void SDiagsWriter::EmitPreamble() {
222 // EmitRawStringContents("CLANG_DIAGS");
223 // Stream.Emit(Version, 32);
224
225  // Emit the file header.
226  Stream.Emit((unsigned)'D', 8);
227  Stream.Emit((unsigned) Version, 32 - 8);
228
229  EmitBlockInfoBlock();
230}
231
232static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) {
233  using namespace llvm;
234  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line.
235  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column.
236  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset;
237}
238void SDiagsWriter::EmitBlockInfoBlock() {
239  Stream.EnterBlockInfoBlock(3);
240
241  // ==---------------------------------------------------------------------==//
242  // The subsequent records and Abbrevs are for the "Diagnostic" block.
243  // ==---------------------------------------------------------------------==//
244
245  EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record);
246  EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record);
247  EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record);
248  EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record);
249  EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record);
250
251  // Emit Abbrevs.
252  using namespace llvm;
253
254  // Emit abbreviation for RECORD_DIAG.
255  BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
256  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));
257  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3));  // Diag level.
258  AddSourceLocationAbbrev(Abbrev);
259  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category.
260  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
261  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
262  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.
263  Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
264
265  // Emit abbrevation for RECORD_CATEGORY.
266  Abbrev = new BitCodeAbbrev();
267  Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
268  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID.
269  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8));  // Text size.
270  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));      // Category text.
271  Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
272
273  // Emit abbrevation for RECORD_SOURCE_RANGE.
274  Abbrev = new BitCodeAbbrev();
275  Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE));
276  AddSourceLocationAbbrev(Abbrev);
277  AddSourceLocationAbbrev(Abbrev);
278  Abbrevs.set(RECORD_SOURCE_RANGE,
279              Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
280
281  // Emit the abbreviation for RECORD_DIAG_FLAG.
282  Abbrev = new BitCodeAbbrev();
283  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));
284  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
285  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
286  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.
287  Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,
288                                                           Abbrev));
289
290  // ==---------------------------------------------------------------------==//
291  // The subsequent records and Abbrevs are for the "Strings" block.
292  // ==---------------------------------------------------------------------==//
293
294  EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record);
295  EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record);
296
297  Abbrev = new BitCodeAbbrev();
298  Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));
299  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Size.
300  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Modifcation time.
301  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
302  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
303  Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
304                                                          Abbrev));
305
306  Stream.ExitBlock();
307}
308
309unsigned SDiagsWriter::getEmitCategory(unsigned int DiagID) {
310  unsigned category = DiagnosticIDs::getCategoryNumberForDiag(DiagID);
311
312  if (Categories.count(category))
313    return category;
314
315  Categories.insert(category);
316
317  // We use a local version of 'Record' so that we can be generating
318  // another record when we lazily generate one for the category entry.
319  RecordData Record;
320  Record.push_back(RECORD_CATEGORY);
321  Record.push_back(category);
322  StringRef catName = DiagnosticIDs::getCategoryNameFromID(category);
323  Record.push_back(catName.size());
324  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName);
325
326  return category;
327}
328
329unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,
330                                             const Diagnostic &Info) {
331  if (DiagLevel == DiagnosticsEngine::Note)
332    return 0; // No flag for notes.
333
334  StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
335  if (FlagName.empty())
336    return 0;
337
338  // Here we assume that FlagName points to static data whose pointer
339  // value is fixed.  This allows us to unique by diagnostic groups.
340  const void *data = FlagName.data();
341  std::pair<unsigned, StringRef> &entry = DiagFlags[data];
342  if (entry.first == 0) {
343    entry.first = DiagFlags.size();
344    entry.second = FlagName;
345
346    // Lazily emit the string in a separate record.
347    RecordData Record;
348    Record.push_back(RECORD_DIAG_FLAG);
349    Record.push_back(entry.first);
350    Record.push_back(FlagName.size());
351    Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
352                              Record, FlagName);
353  }
354
355  return entry.first;
356}
357
358void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) {
359  for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I)
360    Stream.Emit(*I, 8);
361}
362
363void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
364                                    const Diagnostic &Info) {
365
366  if (DiagLevel != DiagnosticsEngine::Note) {
367    if (inNonNoteDiagnostic) {
368      // We have encountered a non-note diagnostic.  Finish up the previous
369      // diagnostic block before starting a new one.
370      Stream.ExitBlock();
371    }
372    inNonNoteDiagnostic = true;
373  }
374
375  Stream.EnterSubblock(BLOCK_DIAG, 3);
376
377  // Emit the RECORD_DIAG record.
378  Record.clear();
379  Record.push_back(RECORD_DIAG);
380  Record.push_back(DiagLevel);
381  AddLocToRecord(Diags.getSourceManager(), Info.getLocation(), Record);
382  // Emit the category string lazily and get the category ID.
383  Record.push_back(getEmitCategory(Info.getID()));
384  // Emit the diagnostic flag string lazily and get the mapped ID.
385  Record.push_back(getEmitDiagnosticFlag(DiagLevel, Info));
386
387
388  diagBuf.clear();
389  Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
390  Record.push_back(diagBuf.str().size());
391  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());
392
393  ArrayRef<CharSourceRange> Ranges = Info.getRanges();
394  for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end();
395       it != ei; ++it) {
396    EmitCharSourceRange(*it);
397  }
398
399  // FIXME: emit fixits
400
401  if (DiagLevel == DiagnosticsEngine::Note) {
402    // Notes currently cannot have child diagnostics.  Complete the
403    // diagnostic now.
404    Stream.ExitBlock();
405  }
406}
407
408template <typename T>
409static void populateAndSort(std::vector<T> &scribble,
410                            llvm::DenseSet<T> &set) {
411  scribble.clear();
412
413  for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end();
414       it != ei; ++it)
415    scribble.push_back(*it);
416
417  // Sort 'scribble' so we always have a deterministic ordering in the
418  // serialized file.
419  std::sort(scribble.begin(), scribble.end());
420}
421
422void SDiagsWriter::EmitCategoriesAndFileNames() {
423  if (Categories.empty() && Files.empty())
424    return;
425
426  BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS);
427
428  // Emit the file names.
429  {
430    std::vector<FileID> scribble;
431    populateAndSort(scribble, Files);
432    for (std::vector<FileID>::iterator it = scribble.begin(),
433         ei = scribble.end(); it != ei; ++it) {
434      SourceManager &SM = Diags.getSourceManager();
435      const FileEntry *FE = SM.getFileEntryForID(*it);
436      StringRef Name = FE->getName();
437
438      Record.clear();
439      Record.push_back(FE->getSize());
440      Record.push_back(FE->getModificationTime());
441      Record.push_back(Name.size());
442      Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name);
443    }
444  }
445}
446
447void SDiagsWriter::EndSourceFile() {
448  if (inNonNoteDiagnostic) {
449    // Finish off any diagnostics we were in the process of emitting.
450    Stream.ExitBlock();
451    inNonNoteDiagnostic = false;
452  }
453
454  EmitCategoriesAndFileNames();
455
456  // Write the generated bitstream to "Out".
457  OS->write((char *)&Buffer.front(), Buffer.size());
458  OS->flush();
459
460  OS.reset(0);
461}
462
463