blob: 4a7c8cf60cfff45bfbca32d301b47e3f3d5fc7ef [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)
64 : Stream(Buffer), OS(os), Diags(diags)
65 {
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;
123
124 enum BlockIDs {
125 /// \brief The DIAG block, which acts as a container around a diagnostic.
126 BLOCK_DIAG = llvm::bitc::FIRST_APPLICATION_BLOCKID,
127 /// \brief The STRINGS block, which contains strings
128 /// from multiple diagnostics.
129 BLOCK_STRINGS
130 };
131
132 enum RecordIDs {
133 RECORD_DIAG = 1,
134 RECORD_DIAG_FLAG,
135 RECORD_CATEGORY,
136 RECORD_FILENAME
137 };
138
139};
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
187/// \brief Emits the preamble of the diagnostics file.
188void SDiagsWriter::EmitPreamble() {
189 // EmitRawStringContents("CLANG_DIAGS");
190 // Stream.Emit(Version, 32);
191
192 // Emit the file header.
193 Stream.Emit((unsigned)'D', 8);
194 Stream.Emit((unsigned)'I', 8);
195 Stream.Emit((unsigned)'A', 8);
196 Stream.Emit((unsigned)'G', 8);
197
198 EmitBlockInfoBlock();
199}
200
201void SDiagsWriter::EmitBlockInfoBlock() {
202 Stream.EnterBlockInfoBlock(3);
203
204 // ==---------------------------------------------------------------------==//
205 // The subsequent records and Abbrevs are for the "Diagnostic" block.
206 // ==---------------------------------------------------------------------==//
207
208 EmitBlockID(BLOCK_DIAG, "Diagnostic", Stream, Record);
209 EmitRecordID(RECORD_DIAG, "Diagnostic Info", Stream, Record);
210 EmitRecordID(RECORD_DIAG_FLAG, "Diagnostic Flag", Stream, Record);
211
212 // Emit Abbrevs.
213 using namespace llvm;
214
215 // Emit abbreviation for RECORD_DIAG.
216 BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
217 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG));
218 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level.
219 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16-3)); // Category.
220 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
221 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text.
222 Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
223
224
225 // Emit the abbreviation for RECORD_DIAG_FLAG.
226 Abbrev = new BitCodeAbbrev();
227 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG));
228 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
229 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text.
230 Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
231
232 // ==---------------------------------------------------------------------==//
233 // The subsequent records and Abbrevs are for the "Strings" block.
234 // ==---------------------------------------------------------------------==//
235
236 EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record);
237 EmitRecordID(RECORD_CATEGORY, "Category Name", Stream, Record);
238 EmitRecordID(RECORD_FILENAME, "File Name", Stream, Record);
239
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();
248 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY));
249 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size.
250 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text.
251 Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS,
252 Abbrev));
253
254 Stream.ExitBlock();
255}
256
257void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) {
258 for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I)
259 Stream.Emit(*I, 8);
260}
261
262void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
263 const Diagnostic &Info) {
264
265 BlockEnterExit DiagBlock(Stream, BLOCK_DIAG);
266
267 // Emit the RECORD_DIAG record.
268 Record.clear();
269 Record.push_back(RECORD_DIAG);
270 Record.push_back(DiagLevel);
271 unsigned category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
272 Record.push_back(category);
273 Categories.insert(category);
274 diagBuf.clear();
275 Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
276 Record.push_back(diagBuf.str().size());
277 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());
278
279 // Emit the RECORD_DIAG_FLAG record.
280 StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
281 if (!FlagName.empty()) {
282 Record.clear();
283 Record.push_back(RECORD_DIAG_FLAG);
284 Record.push_back(FlagName.size());
285 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
286 Record, FlagName.str());
287 }
288
289 // FIXME: emit location
290 // FIXME: emit ranges
291 // FIXME: emit notes
292 // FIXME: emit fixits
293}
294
295template <typename T>
296static void populateAndSort(std::vector<T> &scribble,
297 llvm::DenseSet<T> &set) {
298 scribble.clear();
299
300 for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end();
301 it != ei; ++it)
302 scribble.push_back(*it);
303
304 // Sort 'scribble' so we always have a deterministic ordering in the
305 // serialized file.
306 std::sort(scribble.begin(), scribble.end());
307}
308
309void SDiagsWriter::EmitCategoriesAndFileNames() {
310
311 if (Categories.empty() && Files.empty())
312 return;
313
314 BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS);
315
316 // Emit the category names.
317 {
318 std::vector<unsigned> scribble;
319 populateAndSort(scribble, Categories);
320 for (std::vector<unsigned>::iterator it = scribble.begin(),
321 ei = scribble.end(); it != ei ; ++it) {
322 Record.clear();
323 Record.push_back(RECORD_CATEGORY);
324 StringRef catName = DiagnosticIDs::getCategoryNameFromID(*it);
325 Record.push_back(catName.size());
326 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName);
327 }
328 }
329
330 // Emit the file names.
331 {
332 std::vector<FileID> scribble;
333 populateAndSort(scribble, Files);
334 for (std::vector<FileID>::iterator it = scribble.begin(),
335 ei = scribble.end(); it != ei; ++it) {
336 SourceManager &SM = Diags.getSourceManager();
337 const FileEntry *FE = SM.getFileEntryForID(*it);
338 StringRef Name = FE->getName();
339
340 Record.clear();
341 Record.push_back(Name.size());
342 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name);
343 }
344 }
345
346}
347
348void SDiagsWriter::EndSourceFile() {
349 EmitCategoriesAndFileNames();
350
351 // Write the generated bitstream to "Out".
352 OS->write((char *)&Buffer.front(), Buffer.size());
353 OS->flush();
354
355 OS.reset(0);
356}
357