blob: 3516ad2e7bec732764b6083ab969149de0574e46 [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>
11#include "llvm/Bitcode/BitstreamWriter.h"
12#include "llvm/Support/raw_ostream.h"
13#include "llvm/ADT/StringRef.h"
14#include "llvm/ADT/SmallString.h"
15#include "llvm/ADT/DenseSet.h"
16#include "clang/Basic/SourceManager.h"
17#include "clang/Basic/FileManager.h"
18#include "clang/Basic/Diagnostic.h"
19#include "clang/Basic/Version.h"
20#include "clang/Frontend/SerializedDiagnosticPrinter.h"
21
22using namespace clang;
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)
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
94 /// \brief The version of the diagnostics file.
95 enum { Version = 1 };
96
97 /// \brief The byte buffer for the serialized content.
98 std::vector<unsigned char> Buffer;
99
100 /// \brief The BitStreamWriter for the serialized diagnostics.
101 llvm::BitstreamWriter Stream;
102
103 /// \brief The name of the diagnostics file.
104 llvm::OwningPtr<llvm::raw_ostream> OS;
105
106 /// \brief The DiagnosticsEngine tied to all diagnostic locations.
107 DiagnosticsEngine &Diags;
108
109 /// \brief The set of constructed record abbreviations.
110 AbbreviationMap Abbrevs;
111
112 /// \brief A utility buffer for constructing record content.
113 RecordData Record;
114
115 /// \brief A text buffer for rendering diagnostic text.
116 llvm::SmallString<256> diagBuf;
117
118 /// \brief The collection of diagnostic categories used.
119 llvm::DenseSet<unsigned> Categories;
120
121 /// \brief The collection of files used.
122 llvm::DenseSet<FileID> Files;
Ted Kremenek45d92752011-11-05 00:09:50 +0000123
124 typedef llvm::DenseMap<const void *, std::pair<unsigned, llvm::StringRef> >
125 DiagFlagsTy;
126
127 /// \brief Map for uniquing strings.
128 DiagFlagsTy DiagFlags;
Ted Kremenek78002122011-10-29 00:12:39 +0000129
Ted Kremenek59b61612011-11-05 00:09:47 +0000130 /// \brief Flag indicating whether or not we are in the process of
131 /// emitting a non-note diagnostic.
132 bool inNonNoteDiagnostic;
133
Ted Kremenek78002122011-10-29 00:12:39 +0000134 enum BlockIDs {
135 /// \brief The DIAG block, which acts as a container around a diagnostic.
136 BLOCK_DIAG = llvm::bitc::FIRST_APPLICATION_BLOCKID,
137 /// \brief The STRINGS block, which contains strings
138 /// from multiple diagnostics.
139 BLOCK_STRINGS
140 };
141
142 enum RecordIDs {
143 RECORD_DIAG = 1,
144 RECORD_DIAG_FLAG,
145 RECORD_CATEGORY,
146 RECORD_FILENAME
147 };
Ted Kremenek78002122011-10-29 00:12:39 +0000148};
149} // end anonymous namespace
150
151namespace clang {
152namespace serialized_diags {
153DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) {
154 return new SDiagsWriter(Diags, OS);
155}
156} // end namespace serialized_diags
157} // end namespace clang
158
159//===----------------------------------------------------------------------===//
160// Serialization methods.
161//===----------------------------------------------------------------------===//
162
163/// \brief Emits a block ID in the BLOCKINFO block.
164static void EmitBlockID(unsigned ID, const char *Name,
165 llvm::BitstreamWriter &Stream,
166 RecordDataImpl &Record) {
167 Record.clear();
168 Record.push_back(ID);
169 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);
170
171 // Emit the block name if present.
172 if (Name == 0 || Name[0] == 0)
173 return;
174
175 Record.clear();
176
177 while (*Name)
178 Record.push_back(*Name++);
179
180 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
181}
182
183/// \brief Emits a record ID in the BLOCKINFO block.
184static void EmitRecordID(unsigned ID, const char *Name,
185 llvm::BitstreamWriter &Stream,
186 RecordDataImpl &Record){
187 Record.clear();
188 Record.push_back(ID);
189
190 while (*Name)
191 Record.push_back(*Name++);
192
193 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
194}
195
196/// \brief Emits the preamble of the diagnostics file.
197void SDiagsWriter::EmitPreamble() {
198 // EmitRawStringContents("CLANG_DIAGS");
199 // Stream.Emit(Version, 32);
200
201 // Emit the file header.
202 Stream.Emit((unsigned)'D', 8);
Ted Kremenek069f9c22011-11-05 00:09:53 +0000203 Stream.Emit((unsigned) Version, 32 - 8);
204
Ted Kremenek78002122011-10-29 00:12:39 +0000205 EmitBlockInfoBlock();
206}
207
208void SDiagsWriter::EmitBlockInfoBlock() {
209 Stream.EnterBlockInfoBlock(3);
210
211 // ==---------------------------------------------------------------------==//
212 // The subsequent records and Abbrevs are for the "Diagnostic" block.
213 // ==---------------------------------------------------------------------==//
214
Ted Kremenek45d92752011-11-05 00:09:50 +0000215 EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record);
216 EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record);
Ted Kremenek78002122011-10-29 00:12:39 +0000217
218 // Emit Abbrevs.
219 using namespace llvm;
220
221 // Emit abbreviation for RECORD_DIAG.
222 BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
223 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));
Ted Kremenek45d92752011-11-05 00:09:50 +0000224 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level.
225 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category.
226 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
Ted Kremenek78002122011-10-29 00:12:39 +0000227 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
228 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.
229 Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
230
Ted Kremenek78002122011-10-29 00:12:39 +0000231 // ==---------------------------------------------------------------------==//
232 // The subsequent records and Abbrevs are for the "Strings" block.
233 // ==---------------------------------------------------------------------==//
234
235 EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record);
Ted Kremenek45d92752011-11-05 00:09:50 +0000236 EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record);
237 EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record);
238 EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record);
Ted Kremenek78002122011-10-29 00:12:39 +0000239
240 Abbrev = new BitCodeAbbrev();
241 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
242 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size.
243 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text.
244 Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
245 Abbrev));
246
247 Abbrev = new BitCodeAbbrev();
Ted Kremenek28eac522011-11-05 00:09:43 +0000248 Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME));
249 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Size.
250 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Modifcation time.
Ted Kremenek78002122011-10-29 00:12:39 +0000251 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
252 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
253 Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
254 Abbrev));
Ted Kremenek45d92752011-11-05 00:09:50 +0000255
256 // Emit the abbreviation for RECORD_DIAG_FLAG.
257 Abbrev = new BitCodeAbbrev();
258 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));
259 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID.
260 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
261 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.
262 Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
263 Abbrev));
Ted Kremenek78002122011-10-29 00:12:39 +0000264
265 Stream.ExitBlock();
266}
267
268void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) {
269 for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I)
270 Stream.Emit(*I, 8);
271}
272
273void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
274 const Diagnostic &Info) {
275
Ted Kremenek59b61612011-11-05 00:09:47 +0000276 if (DiagLevel != DiagnosticsEngine::Note) {
277 if (inNonNoteDiagnostic) {
278 // We have encountered a non-note diagnostic. Finish up the previous
279 // diagnostic block before starting a new one.
280 Stream.ExitBlock();
281 }
282 inNonNoteDiagnostic = true;
283 }
284
285 Stream.EnterSubblock(BLOCK_DIAG, 3);
Ted Kremenek78002122011-10-29 00:12:39 +0000286
287 // Emit the RECORD_DIAG record.
288 Record.clear();
289 Record.push_back(RECORD_DIAG);
290 Record.push_back(DiagLevel);
291 unsigned category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
292 Record.push_back(category);
293 Categories.insert(category);
Ted Kremenek45d92752011-11-05 00:09:50 +0000294 if (DiagLevel == DiagnosticsEngine::Note)
295 Record.push_back(0); // No flag for notes.
296 else {
297 StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
298 if (FlagName.empty())
299 Record.push_back(0);
300 else {
301 // Here we assume that FlagName points to static data whose pointer
302 // value is fixed.
303 const void *data = FlagName.data();
304 std::pair<unsigned, StringRef> &entry = DiagFlags[data];
305 if (entry.first == 0) {
306 entry.first = DiagFlags.size();
307 entry.second = FlagName;
308 }
309 Record.push_back(entry.first);
310 }
311 }
312
Ted Kremenek78002122011-10-29 00:12:39 +0000313 diagBuf.clear();
314 Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
315 Record.push_back(diagBuf.str().size());
316 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());
Ted Kremenek78002122011-10-29 00:12:39 +0000317
318 // FIXME: emit location
319 // FIXME: emit ranges
Ted Kremenek78002122011-10-29 00:12:39 +0000320 // FIXME: emit fixits
Ted Kremenek59b61612011-11-05 00:09:47 +0000321
322 if (DiagLevel == DiagnosticsEngine::Note) {
323 // Notes currently cannot have child diagnostics. Complete the
324 // diagnostic now.
325 Stream.ExitBlock();
326 }
Ted Kremenek78002122011-10-29 00:12:39 +0000327}
328
329template <typename T>
330static void populateAndSort(std::vector<T> &scribble,
331 llvm::DenseSet<T> &set) {
332 scribble.clear();
333
334 for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end();
335 it != ei; ++it)
336 scribble.push_back(*it);
337
338 // Sort 'scribble' so we always have a deterministic ordering in the
339 // serialized file.
340 std::sort(scribble.begin(), scribble.end());
341}
342
343void SDiagsWriter::EmitCategoriesAndFileNames() {
344
345 if (Categories.empty() && Files.empty())
346 return;
347
348 BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS);
349
350 // Emit the category names.
351 {
352 std::vector<unsigned> scribble;
353 populateAndSort(scribble, Categories);
354 for (std::vector<unsigned>::iterator it = scribble.begin(),
355 ei = scribble.end(); it != ei ; ++it) {
356 Record.clear();
357 Record.push_back(RECORD_CATEGORY);
358 StringRef catName = DiagnosticIDs::getCategoryNameFromID(*it);
359 Record.push_back(catName.size());
360 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName);
361 }
362 }
363
364 // Emit the file names.
365 {
366 std::vector<FileID> scribble;
367 populateAndSort(scribble, Files);
368 for (std::vector<FileID>::iterator it = scribble.begin(),
369 ei = scribble.end(); it != ei; ++it) {
370 SourceManager &SM = Diags.getSourceManager();
371 const FileEntry *FE = SM.getFileEntryForID(*it);
372 StringRef Name = FE->getName();
373
374 Record.clear();
Ted Kremenek28eac522011-11-05 00:09:43 +0000375 Record.push_back(FE->getSize());
376 Record.push_back(FE->getModificationTime());
Ted Kremenek78002122011-10-29 00:12:39 +0000377 Record.push_back(Name.size());
378 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name);
379 }
380 }
381
Ted Kremenek45d92752011-11-05 00:09:50 +0000382 // Emit the flag strings.
383 {
384 std::vector<StringRef> scribble;
385 scribble.resize(DiagFlags.size());
386
387 for (DiagFlagsTy::iterator it = DiagFlags.begin(), ei = DiagFlags.end();
388 it != ei; ++it) {
389 scribble[it->second.first - 1] = it->second.second;
390 }
391 for (unsigned i = 0, n = scribble.size(); i != n; ++i) {
392 Record.clear();
393 Record.push_back(RECORD_DIAG_FLAG);
394 Record.push_back(i+1);
395 StringRef FlagName = scribble[i];
396 Record.push_back(FlagName.size());
397 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
398 Record, FlagName);
399 }
400 }
Ted Kremenek78002122011-10-29 00:12:39 +0000401}
402
403void SDiagsWriter::EndSourceFile() {
Ted Kremenek59b61612011-11-05 00:09:47 +0000404 if (inNonNoteDiagnostic) {
405 // Finish off any diagnostics we were in the process of emitting.
406 Stream.ExitBlock();
407 inNonNoteDiagnostic = false;
408 }
409
Ted Kremenek78002122011-10-29 00:12:39 +0000410 EmitCategoriesAndFileNames();
411
412 // Write the generated bitstream to "Out".
413 OS->write((char *)&Buffer.front(), Buffer.size());
414 OS->flush();
415
416 OS.reset(0);
417}
418