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