blob: 12448650a30f00962507a734108f4c40e2f322fd [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 Kremenek0dbadc42011-11-05 00:10:04 +000097 /// \brief Emit the string information for a category.
98 void EmitCategory(unsigned CatID);
99
Ted Kremenek78002122011-10-29 00:12:39 +0000100 /// \brief The version of the diagnostics file.
101 enum { Version = 1 };
102
103 /// \brief The byte buffer for the serialized content.
104 std::vector<unsigned char> Buffer;
105
106 /// \brief The BitStreamWriter for the serialized diagnostics.
107 llvm::BitstreamWriter Stream;
108
109 /// \brief The name of the diagnostics file.
110 llvm::OwningPtr<llvm::raw_ostream> OS;
111
112 /// \brief The DiagnosticsEngine tied to all diagnostic locations.
113 DiagnosticsEngine &Diags;
114
115 /// \brief The set of constructed record abbreviations.
116 AbbreviationMap Abbrevs;
117
118 /// \brief A utility buffer for constructing record content.
119 RecordData Record;
120
121 /// \brief A text buffer for rendering diagnostic text.
122 llvm::SmallString<256> diagBuf;
123
124 /// \brief The collection of diagnostic categories used.
125 llvm::DenseSet<unsigned> Categories;
126
127 /// \brief The collection of files used.
128 llvm::DenseSet<FileID> Files;
Ted Kremenek45d92752011-11-05 00:09:50 +0000129
130 typedef llvm::DenseMap<const void *, std::pair<unsigned, llvm::StringRef> >
131 DiagFlagsTy;
132
133 /// \brief Map for uniquing strings.
134 DiagFlagsTy DiagFlags;
Ted Kremenek78002122011-10-29 00:12:39 +0000135
Ted Kremenek59b61612011-11-05 00:09:47 +0000136 /// \brief Flag indicating whether or not we are in the process of
137 /// emitting a non-note diagnostic.
138 bool inNonNoteDiagnostic;
Ted Kremenek78002122011-10-29 00:12:39 +0000139};
140} // end anonymous namespace
141
142namespace clang {
143namespace serialized_diags {
144DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) {
145 return new SDiagsWriter(Diags, OS);
146}
147} // end namespace serialized_diags
148} // end namespace clang
149
150//===----------------------------------------------------------------------===//
151// Serialization methods.
152//===----------------------------------------------------------------------===//
153
154/// \brief Emits a block ID in the BLOCKINFO block.
155static void EmitBlockID(unsigned ID, const char *Name,
156 llvm::BitstreamWriter &Stream,
157 RecordDataImpl &Record) {
158 Record.clear();
159 Record.push_back(ID);
160 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);
161
162 // Emit the block name if present.
163 if (Name == 0 || Name[0] == 0)
164 return;
165
166 Record.clear();
167
168 while (*Name)
169 Record.push_back(*Name++);
170
171 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
172}
173
174/// \brief Emits a record ID in the BLOCKINFO block.
175static void EmitRecordID(unsigned ID, const char *Name,
176 llvm::BitstreamWriter &Stream,
177 RecordDataImpl &Record){
178 Record.clear();
179 Record.push_back(ID);
180
181 while (*Name)
182 Record.push_back(*Name++);
183
184 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
185}
186
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000187static void AddLocToRecord(SourceManager &SM,
188 SourceLocation Loc,
189 RecordDataImpl &Record) {
190 if (Loc.isInvalid()) {
191 // Emit a "sentinel" location.
192 Record.push_back(~(unsigned)0); // Line.
193 Record.push_back(~(unsigned)0); // Column.
194 Record.push_back(~(unsigned)0); // Offset.
195 return;
196 }
197
198 Loc = SM.getSpellingLoc(Loc);
199 Record.push_back(SM.getSpellingLineNumber(Loc));
200 Record.push_back(SM.getSpellingColumnNumber(Loc));
201
202 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
203 FileID FID = LocInfo.first;
204 unsigned FileOffset = LocInfo.second;
205 Record.push_back(FileOffset);
206}
207
208void SDiagsWriter::EmitCharSourceRange(CharSourceRange R) {
209 Record.clear();
210 Record.push_back(RECORD_SOURCE_RANGE);
211 AddLocToRecord(Diags.getSourceManager(), R.getBegin(), Record);
212 AddLocToRecord(Diags.getSourceManager(), R.getEnd(), Record);
213 Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_SOURCE_RANGE), Record);
214}
215
Ted Kremenek78002122011-10-29 00:12:39 +0000216/// \brief Emits the preamble of the diagnostics file.
217void SDiagsWriter::EmitPreamble() {
218 // EmitRawStringContents("CLANG_DIAGS");
219 // Stream.Emit(Version, 32);
220
221 // Emit the file header.
222 Stream.Emit((unsigned)'D', 8);
Ted Kremenek069f9c22011-11-05 00:09:53 +0000223 Stream.Emit((unsigned) Version, 32 - 8);
224
Ted Kremenek78002122011-10-29 00:12:39 +0000225 EmitBlockInfoBlock();
226}
227
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000228static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) {
229 using namespace llvm;
230 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line.
231 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column.
232 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset;
233}
Ted Kremenek78002122011-10-29 00:12:39 +0000234void SDiagsWriter::EmitBlockInfoBlock() {
235 Stream.EnterBlockInfoBlock(3);
236
237 // ==---------------------------------------------------------------------==//
238 // The subsequent records and Abbrevs are for the "Diagnostic" block.
239 // ==---------------------------------------------------------------------==//
240
Ted Kremenek45d92752011-11-05 00:09:50 +0000241 EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record);
242 EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record);
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000243 EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record);
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000244 EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record);
Ted Kremenek78002122011-10-29 00:12:39 +0000245
246 // Emit Abbrevs.
247 using namespace llvm;
248
249 // Emit abbreviation for RECORD_DIAG.
250 BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
251 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));
Ted Kremenek45d92752011-11-05 00:09:50 +0000252 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level.
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000253 AddSourceLocationAbbrev(Abbrev);
Ted Kremenek45d92752011-11-05 00:09:50 +0000254 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category.
255 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
Ted Kremenek78002122011-10-29 00:12:39 +0000256 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
257 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.
258 Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000259
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000260 // Emit abbrevation for RECORD_CATEGORY.
261 Abbrev = new BitCodeAbbrev();
262 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
263 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID.
264 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size.
265 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text.
266 Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
267
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000268 // Emit abbrevation for RECORD_SOURCE_RANGE.
269 Abbrev = new BitCodeAbbrev();
270 Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE));
271 AddSourceLocationAbbrev(Abbrev);
272 AddSourceLocationAbbrev(Abbrev);
273 Abbrevs.set(RECORD_SOURCE_RANGE,
274 Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
Ted Kremenek78002122011-10-29 00:12:39 +0000275
Ted Kremenek78002122011-10-29 00:12:39 +0000276 // ==---------------------------------------------------------------------==//
277 // The subsequent records and Abbrevs are for the "Strings" block.
278 // ==---------------------------------------------------------------------==//
279
280 EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record);
Ted Kremenek45d92752011-11-05 00:09:50 +0000281 EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record);
282 EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record);
Ted Kremenek78002122011-10-29 00:12:39 +0000283
284 Abbrev = new BitCodeAbbrev();
Ted Kremenek28eac522011-11-05 00:09:43 +0000285 Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));
286 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Size.
287 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Modifcation time.
Ted Kremenek78002122011-10-29 00:12:39 +0000288 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
289 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
290 Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
291 Abbrev));
Ted Kremenek45d92752011-11-05 00:09:50 +0000292
293 // Emit the abbreviation for RECORD_DIAG_FLAG.
294 Abbrev = new BitCodeAbbrev();
295 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));
296 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
297 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
298 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.
299 Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
300 Abbrev));
Ted Kremenek78002122011-10-29 00:12:39 +0000301
302 Stream.ExitBlock();
303}
304
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000305void SDiagsWriter::EmitCategory(unsigned int CatID) {
306 if (Categories.count(CatID))
307 return;
308
309 Categories.insert(CatID);
310
311 // We use a local version of 'Record' so that we can be generating
312 // another record when we lazily generate one for the category entry.
313 RecordData Record;
314 Record.push_back(RECORD_CATEGORY);
315 Record.push_back(CatID);
316 StringRef catName = DiagnosticIDs::getCategoryNameFromID(CatID);
317 Record.push_back(catName.size());
318 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName);
319}
320
Ted Kremenek78002122011-10-29 00:12:39 +0000321void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) {
322 for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I)
323 Stream.Emit(*I, 8);
324}
325
326void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
327 const Diagnostic &Info) {
328
Ted Kremenek59b61612011-11-05 00:09:47 +0000329 if (DiagLevel != DiagnosticsEngine::Note) {
330 if (inNonNoteDiagnostic) {
331 // We have encountered a non-note diagnostic. Finish up the previous
332 // diagnostic block before starting a new one.
333 Stream.ExitBlock();
334 }
335 inNonNoteDiagnostic = true;
336 }
337
338 Stream.EnterSubblock(BLOCK_DIAG, 3);
Ted Kremenek78002122011-10-29 00:12:39 +0000339
340 // Emit the RECORD_DIAG record.
341 Record.clear();
342 Record.push_back(RECORD_DIAG);
343 Record.push_back(DiagLevel);
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000344 AddLocToRecord(Diags.getSourceManager(), Info.getLocation(), Record);
Ted Kremenek78002122011-10-29 00:12:39 +0000345 unsigned category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
346 Record.push_back(category);
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000347
348 // Emit the category string lazily if we haven't already.
349 EmitCategory(category);
350
Ted Kremenek78002122011-10-29 00:12:39 +0000351 Categories.insert(category);
Ted Kremenek45d92752011-11-05 00:09:50 +0000352 if (DiagLevel == DiagnosticsEngine::Note)
353 Record.push_back(0); // No flag for notes.
354 else {
355 StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
356 if (FlagName.empty())
357 Record.push_back(0);
358 else {
359 // Here we assume that FlagName points to static data whose pointer
360 // value is fixed.
361 const void *data = FlagName.data();
362 std::pair<unsigned, StringRef> &entry = DiagFlags[data];
363 if (entry.first == 0) {
364 entry.first = DiagFlags.size();
365 entry.second = FlagName;
366 }
367 Record.push_back(entry.first);
368 }
369 }
370
Ted Kremenek78002122011-10-29 00:12:39 +0000371 diagBuf.clear();
372 Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
373 Record.push_back(diagBuf.str().size());
374 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());
Ted Kremenek78002122011-10-29 00:12:39 +0000375
Ted Kremenek2a20b4f2011-11-05 00:10:01 +0000376 ArrayRef<CharSourceRange> Ranges = Info.getRanges();
377 for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end();
378 it != ei; ++it) {
379 EmitCharSourceRange(*it);
380 }
381
Ted Kremenek78002122011-10-29 00:12:39 +0000382 // FIXME: emit fixits
Ted Kremenek59b61612011-11-05 00:09:47 +0000383
384 if (DiagLevel == DiagnosticsEngine::Note) {
385 // Notes currently cannot have child diagnostics. Complete the
386 // diagnostic now.
387 Stream.ExitBlock();
388 }
Ted Kremenek78002122011-10-29 00:12:39 +0000389}
390
391template <typename T>
392static void populateAndSort(std::vector<T> &scribble,
393 llvm::DenseSet<T> &set) {
394 scribble.clear();
395
396 for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end();
397 it != ei; ++it)
398 scribble.push_back(*it);
399
400 // Sort 'scribble' so we always have a deterministic ordering in the
401 // serialized file.
402 std::sort(scribble.begin(), scribble.end());
403}
404
405void SDiagsWriter::EmitCategoriesAndFileNames() {
Ted Kremenek78002122011-10-29 00:12:39 +0000406 if (Categories.empty() && Files.empty())
407 return;
Ted Kremenek0dbadc42011-11-05 00:10:04 +0000408
Ted Kremenek78002122011-10-29 00:12:39 +0000409 BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS);
Ted Kremenek78002122011-10-29 00:12:39 +0000410
411 // Emit the file names.
412 {
413 std::vector<FileID> scribble;
414 populateAndSort(scribble, Files);
415 for (std::vector<FileID>::iterator it = scribble.begin(),
416 ei = scribble.end(); it != ei; ++it) {
417 SourceManager &SM = Diags.getSourceManager();
418 const FileEntry *FE = SM.getFileEntryForID(*it);
419 StringRef Name = FE->getName();
420
421 Record.clear();
Ted Kremenek28eac522011-11-05 00:09:43 +0000422 Record.push_back(FE->getSize());
423 Record.push_back(FE->getModificationTime());
Ted Kremenek78002122011-10-29 00:12:39 +0000424 Record.push_back(Name.size());
425 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name);
426 }
427 }
428
Ted Kremenek45d92752011-11-05 00:09:50 +0000429 // Emit the flag strings.
430 {
431 std::vector<StringRef> scribble;
432 scribble.resize(DiagFlags.size());
433
434 for (DiagFlagsTy::iterator it = DiagFlags.begin(), ei = DiagFlags.end();
435 it != ei; ++it) {
436 scribble[it->second.first - 1] = it->second.second;
437 }
438 for (unsigned i = 0, n = scribble.size(); i != n; ++i) {
439 Record.clear();
440 Record.push_back(RECORD_DIAG_FLAG);
441 Record.push_back(i+1);
442 StringRef FlagName = scribble[i];
443 Record.push_back(FlagName.size());
444 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
445 Record, FlagName);
446 }
447 }
Ted Kremenek78002122011-10-29 00:12:39 +0000448}
449
450void SDiagsWriter::EndSourceFile() {
Ted Kremenek59b61612011-11-05 00:09:47 +0000451 if (inNonNoteDiagnostic) {
452 // Finish off any diagnostics we were in the process of emitting.
453 Stream.ExitBlock();
454 inNonNoteDiagnostic = false;
455 }
456
Ted Kremenek78002122011-10-29 00:12:39 +0000457 EmitCategoriesAndFileNames();
458
459 // Write the generated bitstream to "Out".
460 OS->write((char *)&Buffer.front(), Buffer.size());
461 OS->flush();
462
463 OS.reset(0);
464}
465