blob: 4604e699ae42c24c82c33bcad4d0e49284947c28 [file] [log] [blame]
Ted Kremenek78002122011-10-29 00:12:39 +00001//===--- 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>
Ted Kremenek78002122011-10-29 00:12:39 +000011#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;
Ted Kremenekfdd0ced2011-11-05 00:09:57 +000022using namespace clang::serialized_diags;
Ted Kremenek78002122011-10-29 00:12:39 +000023
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)
Ted Kremenek59b61612011-11-05 00:09:47 +000064 : Stream(Buffer), OS(os), Diags(diags), inNonNoteDiagnostic(false)
Ted Kremenek78002122011-10-29 00:12:39 +000065 {
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
Ted Kremenek2a20b4f2011-11-05 00:10:01 +000094 /// \brief Emit a record for a CharSourceRange.
95 void EmitCharSourceRange(CharSourceRange R);
96
Ted Kremenek3baf63d2011-11-05 00:10:07 +000097 /// \brief Emit the string information for the category for a diagnostic.
98 unsigned getEmitCategory(unsigned DiagID);
99
100 /// \brief Emit the string information for diagnostic flags.
101 unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,
102 const Diagnostic &Info);
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000103
Ted Kremenek78002122011-10-29 00:12:39 +0000104 /// \brief The version of the diagnostics file.
105 enum { Version = 1 };
106
107 /// \brief The byte buffer for the serialized content.
108 std::vector<unsigned char> Buffer;
109
110 /// \brief The BitStreamWriter for the serialized diagnostics.
111 llvm::BitstreamWriter Stream;
112
113 /// \brief The name of the diagnostics file.
114 llvm::OwningPtr<llvm::raw_ostream> OS;
115
116 /// \brief The DiagnosticsEngine tied to all diagnostic locations.
117 DiagnosticsEngine &Diags;
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::DenseSet<FileID> Files;
Ted Kremenek45d92752011-11-05 00:09:50 +0000133
134 typedef llvm::DenseMap<const void *, std::pair<unsigned, llvm::StringRef> >
135 DiagFlagsTy;
136
137 /// \brief Map for uniquing strings.
138 DiagFlagsTy DiagFlags;
Ted Kremenek78002122011-10-29 00:12:39 +0000139
Ted Kremenek59b61612011-11-05 00:09:47 +0000140 /// \brief Flag indicating whether or not we are in the process of
141 /// emitting a non-note diagnostic.
142 bool inNonNoteDiagnostic;
Ted Kremenek78002122011-10-29 00:12:39 +0000143};
144} // end anonymous namespace
145
146namespace clang {
147namespace serialized_diags {
148DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) {
149 return new SDiagsWriter(Diags, 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.
159static 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.
179static 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
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000191static void AddLocToRecord(SourceManager &SM,
192 SourceLocation Loc,
193 RecordDataImpl &Record) {
194 if (Loc.isInvalid()) {
195 // Emit a "sentinel" location.
196 Record.push_back(~(unsigned)0); // Line.
197 Record.push_back(~(unsigned)0); // Column.
198 Record.push_back(~(unsigned)0); // Offset.
199 return;
200 }
201
202 Loc = SM.getSpellingLoc(Loc);
203 Record.push_back(SM.getSpellingLineNumber(Loc));
204 Record.push_back(SM.getSpellingColumnNumber(Loc));
205
206 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
207 FileID FID = LocInfo.first;
208 unsigned FileOffset = LocInfo.second;
209 Record.push_back(FileOffset);
210}
211
212void SDiagsWriter::EmitCharSourceRange(CharSourceRange R) {
213 Record.clear();
214 Record.push_back(RECORD_SOURCE_RANGE);
215 AddLocToRecord(Diags.getSourceManager(), R.getBegin(), Record);
216 AddLocToRecord(Diags.getSourceManager(), R.getEnd(), Record);
217 Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_SOURCE_RANGE), Record);
218}
219
Ted Kremenek78002122011-10-29 00:12:39 +0000220/// \brief Emits the preamble of the diagnostics file.
221void SDiagsWriter::EmitPreamble() {
222 // EmitRawStringContents("CLANG_DIAGS");
223 // Stream.Emit(Version, 32);
224
225 // Emit the file header.
226 Stream.Emit((unsigned)'D', 8);
Ted Kremenek069f9c22011-11-05 00:09:53 +0000227 Stream.Emit((unsigned) Version, 32 - 8);
228
Ted Kremenek78002122011-10-29 00:12:39 +0000229 EmitBlockInfoBlock();
230}
231
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000232static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) {
233 using namespace llvm;
234 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line.
235 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column.
236 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset;
237}
Ted Kremenek78002122011-10-29 00:12:39 +0000238void SDiagsWriter::EmitBlockInfoBlock() {
239 Stream.EnterBlockInfoBlock(3);
240
241 // ==---------------------------------------------------------------------==//
242 // The subsequent records and Abbrevs are for the "Diagnostic" block.
243 // ==---------------------------------------------------------------------==//
244
Ted Kremenek45d92752011-11-05 00:09:50 +0000245 EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record);
246 EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record);
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000247 EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record);
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000248 EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record);
Ted Kremenek3baf63d2011-11-05 00:10:07 +0000249 EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record);
250
Ted Kremenek78002122011-10-29 00:12:39 +0000251 // Emit Abbrevs.
252 using namespace llvm;
253
254 // Emit abbreviation for RECORD_DIAG.
255 BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
256 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));
Ted Kremenek45d92752011-11-05 00:09:50 +0000257 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level.
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000258 AddSourceLocationAbbrev(Abbrev);
Ted Kremenek45d92752011-11-05 00:09:50 +0000259 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category.
260 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
Ted Kremenek78002122011-10-29 00:12:39 +0000261 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
262 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.
263 Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000264
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000265 // Emit abbrevation for RECORD_CATEGORY.
266 Abbrev = new BitCodeAbbrev();
267 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
268 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID.
269 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size.
270 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text.
271 Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
272
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000273 // Emit abbrevation for RECORD_SOURCE_RANGE.
274 Abbrev = new BitCodeAbbrev();
275 Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE));
276 AddSourceLocationAbbrev(Abbrev);
277 AddSourceLocationAbbrev(Abbrev);
278 Abbrevs.set(RECORD_SOURCE_RANGE,
279 Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
Ted Kremenek3baf63d2011-11-05 00:10:07 +0000280
281 // Emit the abbreviation for RECORD_DIAG_FLAG.
282 Abbrev = new BitCodeAbbrev();
283 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));
284 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
285 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
286 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.
287 Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,
288 Abbrev));
Ted Kremenek78002122011-10-29 00:12:39 +0000289
Ted Kremenek78002122011-10-29 00:12:39 +0000290 // ==---------------------------------------------------------------------==//
291 // The subsequent records and Abbrevs are for the "Strings" block.
292 // ==---------------------------------------------------------------------==//
293
294 EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record);
Ted Kremenek45d92752011-11-05 00:09:50 +0000295 EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record);
Ted Kremenek78002122011-10-29 00:12:39 +0000296
297 Abbrev = new BitCodeAbbrev();
Ted Kremenek28eac522011-11-05 00:09:43 +0000298 Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));
299 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Size.
300 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Modifcation time.
Ted Kremenek78002122011-10-29 00:12:39 +0000301 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
302 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
303 Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
304 Abbrev));
305
306 Stream.ExitBlock();
307}
308
Ted Kremenek3baf63d2011-11-05 00:10:07 +0000309unsigned SDiagsWriter::getEmitCategory(unsigned int DiagID) {
310 unsigned category = DiagnosticIDs::getCategoryNumberForDiag(DiagID);
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000311
Ted Kremenek3baf63d2011-11-05 00:10:07 +0000312 if (Categories.count(category))
313 return category;
314
315 Categories.insert(category);
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000316
317 // We use a local version of 'Record' so that we can be generating
318 // another record when we lazily generate one for the category entry.
319 RecordData Record;
320 Record.push_back(RECORD_CATEGORY);
Ted Kremenek3baf63d2011-11-05 00:10:07 +0000321 Record.push_back(category);
322 StringRef catName = DiagnosticIDs::getCategoryNameFromID(category);
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000323 Record.push_back(catName.size());
324 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName);
Ted Kremenek3baf63d2011-11-05 00:10:07 +0000325
326 return category;
327}
328
329unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,
330 const Diagnostic &Info) {
331 if (DiagLevel == DiagnosticsEngine::Note)
332 return 0; // No flag for notes.
333
334 StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
335 if (FlagName.empty())
336 return 0;
337
338 // Here we assume that FlagName points to static data whose pointer
339 // value is fixed. This allows us to unique by diagnostic groups.
340 const void *data = FlagName.data();
341 std::pair<unsigned, StringRef> &entry = DiagFlags[data];
342 if (entry.first == 0) {
343 entry.first = DiagFlags.size();
344 entry.second = FlagName;
345
346 // Lazily emit the string in a separate record.
347 RecordData Record;
348 Record.push_back(RECORD_DIAG_FLAG);
349 Record.push_back(entry.first);
350 Record.push_back(FlagName.size());
351 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
352 Record, FlagName);
353 }
354
355 return entry.first;
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000356}
357
Ted Kremenek78002122011-10-29 00:12:39 +0000358void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) {
359 for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I)
360 Stream.Emit(*I, 8);
361}
362
363void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
364 const Diagnostic &Info) {
365
Ted Kremenek59b61612011-11-05 00:09:47 +0000366 if (DiagLevel != DiagnosticsEngine::Note) {
367 if (inNonNoteDiagnostic) {
368 // We have encountered a non-note diagnostic. Finish up the previous
369 // diagnostic block before starting a new one.
370 Stream.ExitBlock();
371 }
372 inNonNoteDiagnostic = true;
373 }
374
375 Stream.EnterSubblock(BLOCK_DIAG, 3);
Ted Kremenek78002122011-10-29 00:12:39 +0000376
377 // Emit the RECORD_DIAG record.
378 Record.clear();
379 Record.push_back(RECORD_DIAG);
380 Record.push_back(DiagLevel);
Ted Kremenek3baf63d2011-11-05 00:10:07 +0000381 AddLocToRecord(Diags.getSourceManager(), Info.getLocation(), Record);
382 // Emit the category string lazily and get the category ID.
383 Record.push_back(getEmitCategory(Info.getID()));
384 // Emit the diagnostic flag string lazily and get the mapped ID.
385 Record.push_back(getEmitDiagnosticFlag(DiagLevel, Info));
386
Ted Kremenek45d92752011-11-05 00:09:50 +0000387
Ted Kremenek78002122011-10-29 00:12:39 +0000388 diagBuf.clear();
389 Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
390 Record.push_back(diagBuf.str().size());
391 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());
Ted Kremenek78002122011-10-29 00:12:39 +0000392
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000393 ArrayRef<CharSourceRange> Ranges = Info.getRanges();
394 for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end();
395 it != ei; ++it) {
396 EmitCharSourceRange(*it);
397 }
398
Ted Kremenek78002122011-10-29 00:12:39 +0000399 // FIXME: emit fixits
Ted Kremenek59b61612011-11-05 00:09:47 +0000400
401 if (DiagLevel == DiagnosticsEngine::Note) {
402 // Notes currently cannot have child diagnostics. Complete the
403 // diagnostic now.
404 Stream.ExitBlock();
405 }
Ted Kremenek78002122011-10-29 00:12:39 +0000406}
407
408template <typename T>
409static void populateAndSort(std::vector<T> &scribble,
410 llvm::DenseSet<T> &set) {
411 scribble.clear();
412
413 for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end();
414 it != ei; ++it)
415 scribble.push_back(*it);
416
417 // Sort 'scribble' so we always have a deterministic ordering in the
418 // serialized file.
419 std::sort(scribble.begin(), scribble.end());
420}
421
422void SDiagsWriter::EmitCategoriesAndFileNames() {
Ted Kremenek78002122011-10-29 00:12:39 +0000423 if (Categories.empty() && Files.empty())
424 return;
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000425
Ted Kremenek78002122011-10-29 00:12:39 +0000426 BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS);
Ted Kremenek78002122011-10-29 00:12:39 +0000427
428 // Emit the file names.
429 {
430 std::vector<FileID> scribble;
431 populateAndSort(scribble, Files);
432 for (std::vector<FileID>::iterator it = scribble.begin(),
433 ei = scribble.end(); it != ei; ++it) {
434 SourceManager &SM = Diags.getSourceManager();
435 const FileEntry *FE = SM.getFileEntryForID(*it);
436 StringRef Name = FE->getName();
437
438 Record.clear();
Ted Kremenek28eac522011-11-05 00:09:43 +0000439 Record.push_back(FE->getSize());
440 Record.push_back(FE->getModificationTime());
Ted Kremenek78002122011-10-29 00:12:39 +0000441 Record.push_back(Name.size());
442 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name);
443 }
444 }
Ted Kremenek78002122011-10-29 00:12:39 +0000445}
446
447void SDiagsWriter::EndSourceFile() {
Ted Kremenek59b61612011-11-05 00:09:47 +0000448 if (inNonNoteDiagnostic) {
449 // Finish off any diagnostics we were in the process of emitting.
450 Stream.ExitBlock();
451 inNonNoteDiagnostic = false;
452 }
453
Ted Kremenek78002122011-10-29 00:12:39 +0000454 EmitCategoriesAndFileNames();
455
456 // Write the generated bitstream to "Out".
457 OS->write((char *)&Buffer.front(), Buffer.size());
458 OS->flush();
459
460 OS.reset(0);
461}
462