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