SerializedDiagnosticPrinter.cpp revision 45d92757f84bbf0fe2d996f482821628fd9cb016
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/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), inNonNoteDiagnostic(false) 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 typedef llvm::DenseMap<const void *, std::pair<unsigned, llvm::StringRef> > 125 DiagFlagsTy; 126 127 /// \brief Map for uniquing strings. 128 DiagFlagsTy DiagFlags; 129 130 /// \brief Flag indicating whether or not we are in the process of 131 /// emitting a non-note diagnostic. 132 bool inNonNoteDiagnostic; 133 134 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 }; 148}; 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); 203 Stream.Emit((unsigned)'I', 8); 204 Stream.Emit((unsigned)'A', 8); 205 Stream.Emit((unsigned)'G', 8); 206 207 EmitBlockInfoBlock(); 208} 209 210void SDiagsWriter::EmitBlockInfoBlock() { 211 Stream.EnterBlockInfoBlock(3); 212 213 // ==---------------------------------------------------------------------==// 214 // The subsequent records and Abbrevs are for the "Diagnostic" block. 215 // ==---------------------------------------------------------------------==// 216 217 EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record); 218 EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record); 219 220 // Emit Abbrevs. 221 using namespace llvm; 222 223 // Emit abbreviation for RECORD_DIAG. 224 BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); 225 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG)); 226 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level. 227 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category. 228 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. 229 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 230 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. 231 Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 232 233 // ==---------------------------------------------------------------------==// 234 // The subsequent records and Abbrevs are for the "Strings" block. 235 // ==---------------------------------------------------------------------==// 236 237 EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record); 238 EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record); 239 EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record); 240 EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record); 241 242 Abbrev = new BitCodeAbbrev(); 243 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); 244 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size. 245 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text. 246 Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS, 247 Abbrev)); 248 249 Abbrev = new BitCodeAbbrev(); 250 Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME)); 251 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Size. 252 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Modifcation time. 253 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 254 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text. 255 Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS, 256 Abbrev)); 257 258 // Emit the abbreviation for RECORD_DIAG_FLAG. 259 Abbrev = new BitCodeAbbrev(); 260 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG)); 261 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. 262 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 263 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text. 264 Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS, 265 Abbrev)); 266 267 Stream.ExitBlock(); 268} 269 270void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) { 271 for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I) 272 Stream.Emit(*I, 8); 273} 274 275void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 276 const Diagnostic &Info) { 277 278 if (DiagLevel != DiagnosticsEngine::Note) { 279 if (inNonNoteDiagnostic) { 280 // We have encountered a non-note diagnostic. Finish up the previous 281 // diagnostic block before starting a new one. 282 Stream.ExitBlock(); 283 } 284 inNonNoteDiagnostic = true; 285 } 286 287 Stream.EnterSubblock(BLOCK_DIAG, 3); 288 289 // Emit the RECORD_DIAG record. 290 Record.clear(); 291 Record.push_back(RECORD_DIAG); 292 Record.push_back(DiagLevel); 293 unsigned category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); 294 Record.push_back(category); 295 Categories.insert(category); 296 if (DiagLevel == DiagnosticsEngine::Note) 297 Record.push_back(0); // No flag for notes. 298 else { 299 StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); 300 if (FlagName.empty()) 301 Record.push_back(0); 302 else { 303 // Here we assume that FlagName points to static data whose pointer 304 // value is fixed. 305 const void *data = FlagName.data(); 306 std::pair<unsigned, StringRef> &entry = DiagFlags[data]; 307 if (entry.first == 0) { 308 entry.first = DiagFlags.size(); 309 entry.second = FlagName; 310 } 311 Record.push_back(entry.first); 312 } 313 } 314 315 diagBuf.clear(); 316 Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text. 317 Record.push_back(diagBuf.str().size()); 318 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str()); 319 320 // FIXME: emit location 321 // FIXME: emit ranges 322 // FIXME: emit fixits 323 324 if (DiagLevel == DiagnosticsEngine::Note) { 325 // Notes currently cannot have child diagnostics. Complete the 326 // diagnostic now. 327 Stream.ExitBlock(); 328 } 329} 330 331template <typename T> 332static void populateAndSort(std::vector<T> &scribble, 333 llvm::DenseSet<T> &set) { 334 scribble.clear(); 335 336 for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end(); 337 it != ei; ++it) 338 scribble.push_back(*it); 339 340 // Sort 'scribble' so we always have a deterministic ordering in the 341 // serialized file. 342 std::sort(scribble.begin(), scribble.end()); 343} 344 345void SDiagsWriter::EmitCategoriesAndFileNames() { 346 347 if (Categories.empty() && Files.empty()) 348 return; 349 350 BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS); 351 352 // Emit the category names. 353 { 354 std::vector<unsigned> scribble; 355 populateAndSort(scribble, Categories); 356 for (std::vector<unsigned>::iterator it = scribble.begin(), 357 ei = scribble.end(); it != ei ; ++it) { 358 Record.clear(); 359 Record.push_back(RECORD_CATEGORY); 360 StringRef catName = DiagnosticIDs::getCategoryNameFromID(*it); 361 Record.push_back(catName.size()); 362 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName); 363 } 364 } 365 366 // Emit the file names. 367 { 368 std::vector<FileID> scribble; 369 populateAndSort(scribble, Files); 370 for (std::vector<FileID>::iterator it = scribble.begin(), 371 ei = scribble.end(); it != ei; ++it) { 372 SourceManager &SM = Diags.getSourceManager(); 373 const FileEntry *FE = SM.getFileEntryForID(*it); 374 StringRef Name = FE->getName(); 375 376 Record.clear(); 377 Record.push_back(FE->getSize()); 378 Record.push_back(FE->getModificationTime()); 379 Record.push_back(Name.size()); 380 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name); 381 } 382 } 383 384 // Emit the flag strings. 385 { 386 std::vector<StringRef> scribble; 387 scribble.resize(DiagFlags.size()); 388 389 for (DiagFlagsTy::iterator it = DiagFlags.begin(), ei = DiagFlags.end(); 390 it != ei; ++it) { 391 scribble[it->second.first - 1] = it->second.second; 392 } 393 for (unsigned i = 0, n = scribble.size(); i != n; ++i) { 394 Record.clear(); 395 Record.push_back(RECORD_DIAG_FLAG); 396 Record.push_back(i+1); 397 StringRef FlagName = scribble[i]; 398 Record.push_back(FlagName.size()); 399 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG), 400 Record, FlagName); 401 } 402 } 403} 404 405void SDiagsWriter::EndSourceFile() { 406 if (inNonNoteDiagnostic) { 407 // Finish off any diagnostics we were in the process of emitting. 408 Stream.ExitBlock(); 409 inNonNoteDiagnostic = false; 410 } 411 412 EmitCategoriesAndFileNames(); 413 414 // Write the generated bitstream to "Out". 415 OS->write((char *)&Buffer.front(), Buffer.size()); 416 OS->flush(); 417 418 OS.reset(0); 419} 420 421