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