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