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