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