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