SerializedDiagnosticPrinter.cpp revision 0b69aa856308f6b35f8b96ef269a482558f2966b
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
26class AbbreviationMap {
27  llvm::DenseMap<unsigned, unsigned> Abbrevs;
28public:
29  AbbreviationMap() {}
30
31  void set(unsigned recordID, unsigned abbrevID) {
32    assert(Abbrevs.find(recordID) == Abbrevs.end()
33           && "Abbreviation already set.");
34    Abbrevs[recordID] = abbrevID;
35  }
36
37  unsigned get(unsigned recordID) {
38    assert(Abbrevs.find(recordID) != Abbrevs.end() &&
39           "Abbreviation not set.");
40    return Abbrevs[recordID];
41  }
42};
43
44typedef llvm::SmallVector<uint64_t, 64> RecordData;
45typedef llvm::SmallVectorImpl<uint64_t> RecordDataImpl;
46
47class SDiagsWriter : public DiagnosticConsumer {
48public:
49  SDiagsWriter(DiagnosticsEngine &diags, llvm::raw_ostream *os)
50    : Stream(Buffer), OS(os), Diags(diags), inNonNoteDiagnostic(false)
51  {
52    EmitPreamble();
53  };
54
55  ~SDiagsWriter() {}
56
57  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
58                        const Diagnostic &Info);
59
60  void EndSourceFile();
61
62  DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
63    // It makes no sense to clone this.
64    return 0;
65  }
66
67private:
68  /// \brief Emit the preamble for the serialized diagnostics.
69  void EmitPreamble();
70
71  /// \brief Emit the BLOCKINFO block.
72  void EmitBlockInfoBlock();
73
74  /// \brief Emit the META data block.
75  void EmitMetaBlock();
76
77  /// \brief Emit a record for a CharSourceRange.
78  void EmitCharSourceRange(CharSourceRange R);
79
80  /// \brief Emit the string information for the category for a diagnostic.
81  unsigned getEmitCategory(unsigned DiagID);
82
83  /// \brief Emit the string information for diagnostic flags.
84  unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,
85                                 const Diagnostic &Info);
86
87  /// \brief Emit (lazily) the file string and retrieved the file identifier.
88  unsigned getEmitFile(SourceLocation Loc);
89
90  /// \brief Add SourceLocation information the specified record.
91  void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record);
92
93  /// \brief Add CharSourceRange information the specified record.
94  void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record);
95
96  /// \brief The version of the diagnostics file.
97  enum { Version = 1 };
98
99  /// \brief The byte buffer for the serialized content.
100  std::vector<unsigned char> Buffer;
101
102  /// \brief The BitStreamWriter for the serialized diagnostics.
103  llvm::BitstreamWriter Stream;
104
105  /// \brief The name of the diagnostics file.
106  llvm::OwningPtr<llvm::raw_ostream> OS;
107
108  /// \brief The DiagnosticsEngine tied to all diagnostic locations.
109  DiagnosticsEngine &Diags;
110
111  /// \brief The set of constructed record abbreviations.
112  AbbreviationMap Abbrevs;
113
114  /// \brief A utility buffer for constructing record content.
115  RecordData Record;
116
117  /// \brief A text buffer for rendering diagnostic text.
118  llvm::SmallString<256> diagBuf;
119
120  /// \brief The collection of diagnostic categories used.
121  llvm::DenseSet<unsigned> Categories;
122
123  /// \brief The collection of files used.
124  llvm::DenseMap<const FileEntry *, unsigned> Files;
125
126  typedef llvm::DenseMap<const void *, std::pair<unsigned, llvm::StringRef> >
127          DiagFlagsTy;
128
129  /// \brief Map for uniquing strings.
130  DiagFlagsTy DiagFlags;
131
132  /// \brief Flag indicating whether or not we are in the process of
133  /// emitting a non-note diagnostic.
134  bool inNonNoteDiagnostic;
135};
136} // end anonymous namespace
137
138namespace clang {
139namespace serialized_diags {
140DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) {
141  return new SDiagsWriter(Diags, OS);
142}
143} // end namespace serialized_diags
144} // end namespace clang
145
146//===----------------------------------------------------------------------===//
147// Serialization methods.
148//===----------------------------------------------------------------------===//
149
150/// \brief Emits a block ID in the BLOCKINFO block.
151static void EmitBlockID(unsigned ID, const char *Name,
152                        llvm::BitstreamWriter &Stream,
153                        RecordDataImpl &Record) {
154  Record.clear();
155  Record.push_back(ID);
156  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);
157
158  // Emit the block name if present.
159  if (Name == 0 || Name[0] == 0)
160    return;
161
162  Record.clear();
163
164  while (*Name)
165    Record.push_back(*Name++);
166
167  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
168}
169
170/// \brief Emits a record ID in the BLOCKINFO block.
171static void EmitRecordID(unsigned ID, const char *Name,
172                         llvm::BitstreamWriter &Stream,
173                         RecordDataImpl &Record){
174  Record.clear();
175  Record.push_back(ID);
176
177  while (*Name)
178    Record.push_back(*Name++);
179
180  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
181}
182
183void SDiagsWriter::AddLocToRecord(SourceLocation Loc,
184                                  RecordDataImpl &Record) {
185  if (Loc.isInvalid()) {
186    // Emit a "sentinel" location.
187    Record.push_back((unsigned) 0); // File.
188    Record.push_back(~(unsigned)0); // Line.
189    Record.push_back(~(unsigned)0); // Column.
190    Record.push_back(~(unsigned)0); // Offset.
191    return;
192  }
193
194  SourceManager &SM = Diags.getSourceManager();
195  Loc = SM.getSpellingLoc(Loc);
196  Record.push_back(getEmitFile(Loc));
197  Record.push_back(SM.getSpellingLineNumber(Loc));
198  Record.push_back(SM.getSpellingColumnNumber(Loc));
199
200  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
201  FileID FID = LocInfo.first;
202  unsigned FileOffset = LocInfo.second;
203  Record.push_back(FileOffset);
204}
205
206void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range,
207                                              RecordDataImpl &Record) {
208  AddLocToRecord(Range.getBegin(), Record);
209  AddLocToRecord(Range.getEnd(), Record);
210}
211
212unsigned SDiagsWriter::getEmitFile(SourceLocation Loc) {
213  SourceManager &SM = Diags.getSourceManager();
214  assert(Loc.isValid());
215  const std::pair<FileID, unsigned> &LocInfo = SM.getDecomposedLoc(Loc);
216  const FileEntry *FE = SM.getFileEntryForID(LocInfo.first);
217  if (!FE)
218    return 0;
219
220  unsigned &entry = Files[FE];
221  if (entry)
222    return entry;
223
224  // Lazily generate the record for the file.
225  entry = Files.size();
226  RecordData Record;
227  Record.push_back(RECORD_FILENAME);
228  Record.push_back(entry);
229  Record.push_back(FE->getSize());
230  Record.push_back(FE->getModificationTime());
231  StringRef Name = FE->getName();
232  Record.push_back(Name.size());
233  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name);
234
235  return entry;
236}
237
238void SDiagsWriter::EmitCharSourceRange(CharSourceRange R) {
239  Record.clear();
240  Record.push_back(RECORD_SOURCE_RANGE);
241  AddCharSourceRangeToRecord(R, Record);
242  Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_SOURCE_RANGE), Record);
243}
244
245/// \brief Emits the preamble of the diagnostics file.
246void SDiagsWriter::EmitPreamble() {
247  // Emit the file header.
248  Stream.Emit((unsigned)'D', 8);
249  Stream.Emit((unsigned)'I', 8);
250  Stream.Emit((unsigned)'A', 8);
251  Stream.Emit((unsigned)'G', 8);
252
253  EmitBlockInfoBlock();
254  EmitMetaBlock();
255}
256
257static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) {
258  using namespace llvm;
259  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID.
260  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line.
261  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column.
262  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset;
263}
264
265static void AddRangeLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) {
266  AddSourceLocationAbbrev(Abbrev);
267  AddSourceLocationAbbrev(Abbrev);
268}
269
270void SDiagsWriter::EmitBlockInfoBlock() {
271  Stream.EnterBlockInfoBlock(3);
272
273  using namespace llvm;
274
275  // ==---------------------------------------------------------------------==//
276  // The subsequent records and Abbrevs are for the "Meta" block.
277  // ==---------------------------------------------------------------------==//
278
279  EmitBlockID(BLOCK_META, "Meta", Stream, Record);
280  EmitRecordID(RECORD_VERSION, "Version", Stream, Record);
281  BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
282  Abbrev->Add(BitCodeAbbrevOp(RECORD_VERSION));
283  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32));
284  Abbrevs.set(RECORD_VERSION, Stream.EmitBlockInfoAbbrev(BLOCK_META, Abbrev));
285
286  // ==---------------------------------------------------------------------==//
287  // The subsequent records and Abbrevs are for the "Diagnostic" block.
288  // ==---------------------------------------------------------------------==//
289
290  EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record);
291  EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record);
292  EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record);
293  EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record);
294  EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record);
295  EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record);
296  EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record);
297
298  // Emit abbreviation for RECORD_DIAG.
299  Abbrev = new BitCodeAbbrev();
300  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));
301  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3));  // Diag level.
302  AddSourceLocationAbbrev(Abbrev);
303  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category.
304  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
305  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
306  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.
307  Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
308
309  // Emit abbrevation for RECORD_CATEGORY.
310  Abbrev = new BitCodeAbbrev();
311  Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
312  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID.
313  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8));  // Text size.
314  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));      // Category text.
315  Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
316
317  // Emit abbrevation for RECORD_SOURCE_RANGE.
318  Abbrev = new BitCodeAbbrev();
319  Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE));
320  AddRangeLocationAbbrev(Abbrev);
321  Abbrevs.set(RECORD_SOURCE_RANGE,
322              Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
323
324  // Emit the abbreviation for RECORD_DIAG_FLAG.
325  Abbrev = new BitCodeAbbrev();
326  Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));
327  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
328  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
329  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.
330  Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,
331                                                           Abbrev));
332
333  // Emit the abbreviation for RECORD_FILENAME.
334  Abbrev = new BitCodeAbbrev();
335  Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));
336  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID.
337  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size.
338  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modifcation time.
339  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
340  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
341  Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,
342                                                          Abbrev));
343
344  // Emit the abbreviation for RECORD_FIXIT.
345  Abbrev = new BitCodeAbbrev();
346  Abbrev->Add(BitCodeAbbrevOp(RECORD_FIXIT));
347  AddRangeLocationAbbrev(Abbrev);
348  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
349  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));      // FixIt text.
350  Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,
351                                                       Abbrev));
352
353  Stream.ExitBlock();
354}
355
356void SDiagsWriter::EmitMetaBlock() {
357  Stream.EnterSubblock(BLOCK_META, 3);
358  Record.clear();
359  Record.push_back(RECORD_VERSION);
360  Record.push_back(Version);
361  Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record);
362  Stream.ExitBlock();
363}
364
365unsigned SDiagsWriter::getEmitCategory(unsigned int DiagID) {
366  unsigned category = DiagnosticIDs::getCategoryNumberForDiag(DiagID);
367
368  if (Categories.count(category))
369    return category;
370
371  Categories.insert(category);
372
373  // We use a local version of 'Record' so that we can be generating
374  // another record when we lazily generate one for the category entry.
375  RecordData Record;
376  Record.push_back(RECORD_CATEGORY);
377  Record.push_back(category);
378  StringRef catName = DiagnosticIDs::getCategoryNameFromID(category);
379  Record.push_back(catName.size());
380  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName);
381
382  return category;
383}
384
385unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,
386                                             const Diagnostic &Info) {
387  if (DiagLevel == DiagnosticsEngine::Note)
388    return 0; // No flag for notes.
389
390  StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
391  if (FlagName.empty())
392    return 0;
393
394  // Here we assume that FlagName points to static data whose pointer
395  // value is fixed.  This allows us to unique by diagnostic groups.
396  const void *data = FlagName.data();
397  std::pair<unsigned, StringRef> &entry = DiagFlags[data];
398  if (entry.first == 0) {
399    entry.first = DiagFlags.size();
400    entry.second = FlagName;
401
402    // Lazily emit the string in a separate record.
403    RecordData Record;
404    Record.push_back(RECORD_DIAG_FLAG);
405    Record.push_back(entry.first);
406    Record.push_back(FlagName.size());
407    Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
408                              Record, FlagName);
409  }
410
411  return entry.first;
412}
413
414void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
415                                    const Diagnostic &Info) {
416
417  if (DiagLevel != DiagnosticsEngine::Note) {
418    if (inNonNoteDiagnostic) {
419      // We have encountered a non-note diagnostic.  Finish up the previous
420      // diagnostic block before starting a new one.
421      Stream.ExitBlock();
422    }
423    inNonNoteDiagnostic = true;
424  }
425
426  Stream.EnterSubblock(BLOCK_DIAG, 4);
427
428  // Emit the RECORD_DIAG record.
429  Record.clear();
430  Record.push_back(RECORD_DIAG);
431  Record.push_back(DiagLevel);
432  AddLocToRecord(Info.getLocation(), Record);
433  // Emit the category string lazily and get the category ID.
434  Record.push_back(getEmitCategory(Info.getID()));
435  // Emit the diagnostic flag string lazily and get the mapped ID.
436  Record.push_back(getEmitDiagnosticFlag(DiagLevel, Info));
437  // Emit the diagnostic text.
438  diagBuf.clear();
439  Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
440  Record.push_back(diagBuf.str().size());
441  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());
442
443  // Emit Source Ranges.
444  ArrayRef<CharSourceRange> Ranges = Info.getRanges();
445  for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end();
446       it != ei; ++it) {
447    EmitCharSourceRange(*it);
448  }
449
450  // Emit FixIts.
451  for (unsigned i = 0, n = Info.getNumFixItHints(); i != n; ++i) {
452    const FixItHint &fix = Info.getFixItHint(i);
453    if (fix.isNull())
454      continue;
455    Record.clear();
456    Record.push_back(RECORD_FIXIT);
457    AddCharSourceRangeToRecord(fix.RemoveRange, Record);
458    Record.push_back(fix.CodeToInsert.size());
459    Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record,
460                              fix.CodeToInsert);
461  }
462
463  if (DiagLevel == DiagnosticsEngine::Note) {
464    // Notes currently cannot have child diagnostics.  Complete the
465    // diagnostic now.
466    Stream.ExitBlock();
467  }
468}
469
470void SDiagsWriter::EndSourceFile() {
471  if (inNonNoteDiagnostic) {
472    // Finish off any diagnostics we were in the process of emitting.
473    Stream.ExitBlock();
474    inNonNoteDiagnostic = false;
475  }
476
477  // Write the generated bitstream to "Out".
478  OS->write((char *)&Buffer.front(), Buffer.size());
479  OS->flush();
480
481  OS.reset(0);
482}
483
484