SerializedDiagnosticPrinter.cpp revision 96dcadec26244e028d59287ea1b249802c8fc2bc
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/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; 22using namespace clang::serialized_diags; 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 a record for a CharSourceRange. 89 void EmitCharSourceRange(CharSourceRange R); 90 91 /// \brief Emit the string information for the category for a diagnostic. 92 unsigned getEmitCategory(unsigned DiagID); 93 94 /// \brief Emit the string information for diagnostic flags. 95 unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, 96 const Diagnostic &Info); 97 98 /// \brief Emit (lazily) the file string and retrieved the file identifier. 99 unsigned getEmitFile(SourceLocation Loc); 100 101 /// \brief Add SourceLocation information the specified record. 102 void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record); 103 104 /// \brief Add CharSourceRange information the specified record. 105 void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record); 106 107 /// \brief The version of the diagnostics file. 108 enum { Version = 1 }; 109 110 /// \brief The byte buffer for the serialized content. 111 std::vector<unsigned char> Buffer; 112 113 /// \brief The BitStreamWriter for the serialized diagnostics. 114 llvm::BitstreamWriter Stream; 115 116 /// \brief The name of the diagnostics file. 117 llvm::OwningPtr<llvm::raw_ostream> OS; 118 119 /// \brief The DiagnosticsEngine tied to all diagnostic locations. 120 DiagnosticsEngine &Diags; 121 122 /// \brief The set of constructed record abbreviations. 123 AbbreviationMap Abbrevs; 124 125 /// \brief A utility buffer for constructing record content. 126 RecordData Record; 127 128 /// \brief A text buffer for rendering diagnostic text. 129 llvm::SmallString<256> diagBuf; 130 131 /// \brief The collection of diagnostic categories used. 132 llvm::DenseSet<unsigned> Categories; 133 134 /// \brief The collection of files used. 135 llvm::DenseMap<const FileEntry *, unsigned> Files; 136 137 typedef llvm::DenseMap<const void *, std::pair<unsigned, llvm::StringRef> > 138 DiagFlagsTy; 139 140 /// \brief Map for uniquing strings. 141 DiagFlagsTy DiagFlags; 142 143 /// \brief Flag indicating whether or not we are in the process of 144 /// emitting a non-note diagnostic. 145 bool inNonNoteDiagnostic; 146}; 147} // end anonymous namespace 148 149namespace clang { 150namespace serialized_diags { 151DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) { 152 return new SDiagsWriter(Diags, OS); 153} 154} // end namespace serialized_diags 155} // end namespace clang 156 157//===----------------------------------------------------------------------===// 158// Serialization methods. 159//===----------------------------------------------------------------------===// 160 161/// \brief Emits a block ID in the BLOCKINFO block. 162static void EmitBlockID(unsigned ID, const char *Name, 163 llvm::BitstreamWriter &Stream, 164 RecordDataImpl &Record) { 165 Record.clear(); 166 Record.push_back(ID); 167 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record); 168 169 // Emit the block name if present. 170 if (Name == 0 || Name[0] == 0) 171 return; 172 173 Record.clear(); 174 175 while (*Name) 176 Record.push_back(*Name++); 177 178 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record); 179} 180 181/// \brief Emits a record ID in the BLOCKINFO block. 182static void EmitRecordID(unsigned ID, const char *Name, 183 llvm::BitstreamWriter &Stream, 184 RecordDataImpl &Record){ 185 Record.clear(); 186 Record.push_back(ID); 187 188 while (*Name) 189 Record.push_back(*Name++); 190 191 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); 192} 193 194void SDiagsWriter::AddLocToRecord(SourceLocation Loc, 195 RecordDataImpl &Record) { 196 if (Loc.isInvalid()) { 197 // Emit a "sentinel" location. 198 Record.push_back((unsigned) 0); // File. 199 Record.push_back(~(unsigned)0); // Line. 200 Record.push_back(~(unsigned)0); // Column. 201 Record.push_back(~(unsigned)0); // Offset. 202 return; 203 } 204 205 SourceManager &SM = Diags.getSourceManager(); 206 Loc = SM.getSpellingLoc(Loc); 207 Record.push_back(getEmitFile(Loc)); 208 Record.push_back(SM.getSpellingLineNumber(Loc)); 209 Record.push_back(SM.getSpellingColumnNumber(Loc)); 210 211 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 212 FileID FID = LocInfo.first; 213 unsigned FileOffset = LocInfo.second; 214 Record.push_back(FileOffset); 215} 216 217void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, 218 RecordDataImpl &Record) { 219 AddLocToRecord(Range.getBegin(), Record); 220 AddLocToRecord(Range.getEnd(), Record); 221} 222 223unsigned SDiagsWriter::getEmitFile(SourceLocation Loc) { 224 SourceManager &SM = Diags.getSourceManager(); 225 assert(Loc.isValid()); 226 const std::pair<FileID, unsigned> &LocInfo = SM.getDecomposedLoc(Loc); 227 const FileEntry *FE = SM.getFileEntryForID(LocInfo.first); 228 if (!FE) 229 return 0; 230 231 unsigned &entry = Files[FE]; 232 if (entry) 233 return entry; 234 235 // Lazily generate the record for the file. 236 entry = Files.size(); 237 RecordData Record; 238 Record.push_back(RECORD_FILENAME); 239 Record.push_back(entry); 240 Record.push_back(FE->getSize()); 241 Record.push_back(FE->getModificationTime()); 242 StringRef Name = FE->getName(); 243 Record.push_back(Name.size()); 244 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name); 245 246 return entry; 247} 248 249void SDiagsWriter::EmitCharSourceRange(CharSourceRange R) { 250 Record.clear(); 251 Record.push_back(RECORD_SOURCE_RANGE); 252 AddCharSourceRangeToRecord(R, Record); 253 Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_SOURCE_RANGE), Record); 254} 255 256/// \brief Emits the preamble of the diagnostics file. 257void SDiagsWriter::EmitPreamble() { 258 // Emit the file header. 259 Stream.Emit((unsigned)'D', 8); 260 Stream.Emit((unsigned) Version, 32 - 8); 261 262 EmitBlockInfoBlock(); 263} 264 265static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) { 266 using namespace llvm; 267 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID. 268 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line. 269 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column. 270 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset; 271} 272 273static void AddRangeLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) { 274 AddSourceLocationAbbrev(Abbrev); 275 AddSourceLocationAbbrev(Abbrev); 276} 277 278void SDiagsWriter::EmitBlockInfoBlock() { 279 Stream.EnterBlockInfoBlock(3); 280 281 // ==---------------------------------------------------------------------==// 282 // The subsequent records and Abbrevs are for the "Diagnostic" block. 283 // ==---------------------------------------------------------------------==// 284 285 EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record); 286 EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record); 287 EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record); 288 EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record); 289 EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record); 290 EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record); 291 EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record); 292 293 // Emit Abbrevs. 294 using namespace llvm; 295 296 // Emit abbreviation for RECORD_DIAG. 297 BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); 298 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG)); 299 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level. 300 AddSourceLocationAbbrev(Abbrev); 301 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category. 302 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. 303 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 304 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. 305 Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 306 307 // Emit abbrevation for RECORD_CATEGORY. 308 Abbrev = new BitCodeAbbrev(); 309 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); 310 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID. 311 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size. 312 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text. 313 Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 314 315 // Emit abbrevation for RECORD_SOURCE_RANGE. 316 Abbrev = new BitCodeAbbrev(); 317 Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE)); 318 AddRangeLocationAbbrev(Abbrev); 319 Abbrevs.set(RECORD_SOURCE_RANGE, 320 Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 321 322 // Emit the abbreviation for RECORD_DIAG_FLAG. 323 Abbrev = new BitCodeAbbrev(); 324 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG)); 325 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. 326 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 327 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text. 328 Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, 329 Abbrev)); 330 331 // Emit the abbreviation for RECORD_FILENAME. 332 Abbrev = new BitCodeAbbrev(); 333 Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME)); 334 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID. 335 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size. 336 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modifcation time. 337 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 338 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text. 339 Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, 340 Abbrev)); 341 342 // Emit the abbreviation for RECORD_FIXIT. 343 Abbrev = new BitCodeAbbrev(); 344 Abbrev->Add(BitCodeAbbrevOp(RECORD_FIXIT)); 345 AddRangeLocationAbbrev(Abbrev); 346 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 347 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // FixIt text. 348 Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, 349 Abbrev)); 350 351 Stream.ExitBlock(); 352} 353 354unsigned SDiagsWriter::getEmitCategory(unsigned int DiagID) { 355 unsigned category = DiagnosticIDs::getCategoryNumberForDiag(DiagID); 356 357 if (Categories.count(category)) 358 return category; 359 360 Categories.insert(category); 361 362 // We use a local version of 'Record' so that we can be generating 363 // another record when we lazily generate one for the category entry. 364 RecordData Record; 365 Record.push_back(RECORD_CATEGORY); 366 Record.push_back(category); 367 StringRef catName = DiagnosticIDs::getCategoryNameFromID(category); 368 Record.push_back(catName.size()); 369 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName); 370 371 return category; 372} 373 374unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, 375 const Diagnostic &Info) { 376 if (DiagLevel == DiagnosticsEngine::Note) 377 return 0; // No flag for notes. 378 379 StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); 380 if (FlagName.empty()) 381 return 0; 382 383 // Here we assume that FlagName points to static data whose pointer 384 // value is fixed. This allows us to unique by diagnostic groups. 385 const void *data = FlagName.data(); 386 std::pair<unsigned, StringRef> &entry = DiagFlags[data]; 387 if (entry.first == 0) { 388 entry.first = DiagFlags.size(); 389 entry.second = FlagName; 390 391 // Lazily emit the string in a separate record. 392 RecordData Record; 393 Record.push_back(RECORD_DIAG_FLAG); 394 Record.push_back(entry.first); 395 Record.push_back(FlagName.size()); 396 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG), 397 Record, FlagName); 398 } 399 400 return entry.first; 401} 402 403void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 404 const Diagnostic &Info) { 405 406 if (DiagLevel != DiagnosticsEngine::Note) { 407 if (inNonNoteDiagnostic) { 408 // We have encountered a non-note diagnostic. Finish up the previous 409 // diagnostic block before starting a new one. 410 Stream.ExitBlock(); 411 } 412 inNonNoteDiagnostic = true; 413 } 414 415 Stream.EnterSubblock(BLOCK_DIAG, 4); 416 417 // Emit the RECORD_DIAG record. 418 Record.clear(); 419 Record.push_back(RECORD_DIAG); 420 Record.push_back(DiagLevel); 421 AddLocToRecord(Info.getLocation(), Record); 422 // Emit the category string lazily and get the category ID. 423 Record.push_back(getEmitCategory(Info.getID())); 424 // Emit the diagnostic flag string lazily and get the mapped ID. 425 Record.push_back(getEmitDiagnosticFlag(DiagLevel, Info)); 426 // Emit the diagnostic text. 427 diagBuf.clear(); 428 Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text. 429 Record.push_back(diagBuf.str().size()); 430 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str()); 431 432 // Emit Source Ranges. 433 ArrayRef<CharSourceRange> Ranges = Info.getRanges(); 434 for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end(); 435 it != ei; ++it) { 436 EmitCharSourceRange(*it); 437 } 438 439 // Emit FixIts. 440 for (unsigned i = 0, n = Info.getNumFixItHints(); i != n; ++i) { 441 const FixItHint &fix = Info.getFixItHint(i); 442 if (fix.isNull()) 443 continue; 444 Record.clear(); 445 Record.push_back(RECORD_FIXIT); 446 AddCharSourceRangeToRecord(fix.RemoveRange, Record); 447 Record.push_back(fix.CodeToInsert.size()); 448 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record, 449 fix.CodeToInsert); 450 } 451 452 if (DiagLevel == DiagnosticsEngine::Note) { 453 // Notes currently cannot have child diagnostics. Complete the 454 // diagnostic now. 455 Stream.ExitBlock(); 456 } 457} 458 459void SDiagsWriter::EndSourceFile() { 460 if (inNonNoteDiagnostic) { 461 // Finish off any diagnostics we were in the process of emitting. 462 Stream.ExitBlock(); 463 inNonNoteDiagnostic = false; 464 } 465 466 // Write the generated bitstream to "Out". 467 OS->write((char *)&Buffer.front(), Buffer.size()); 468 OS->flush(); 469 470 OS.reset(0); 471} 472 473