SerializedDiagnosticPrinter.cpp revision 3baf63d37d0b6267885b584db1106232fc036cb9
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 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 Emit a record for a CharSourceRange. 95 void EmitCharSourceRange(CharSourceRange R); 96 97 /// \brief Emit the string information for the category for a diagnostic. 98 unsigned getEmitCategory(unsigned DiagID); 99 100 /// \brief Emit the string information for diagnostic flags. 101 unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, 102 const Diagnostic &Info); 103 104 /// \brief The version of the diagnostics file. 105 enum { Version = 1 }; 106 107 /// \brief The byte buffer for the serialized content. 108 std::vector<unsigned char> Buffer; 109 110 /// \brief The BitStreamWriter for the serialized diagnostics. 111 llvm::BitstreamWriter Stream; 112 113 /// \brief The name of the diagnostics file. 114 llvm::OwningPtr<llvm::raw_ostream> OS; 115 116 /// \brief The DiagnosticsEngine tied to all diagnostic locations. 117 DiagnosticsEngine &Diags; 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::DenseSet<FileID> 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 146namespace clang { 147namespace serialized_diags { 148DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) { 149 return new SDiagsWriter(Diags, 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. 159static 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. 179static 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 191static void AddLocToRecord(SourceManager &SM, 192 SourceLocation Loc, 193 RecordDataImpl &Record) { 194 if (Loc.isInvalid()) { 195 // Emit a "sentinel" location. 196 Record.push_back(~(unsigned)0); // Line. 197 Record.push_back(~(unsigned)0); // Column. 198 Record.push_back(~(unsigned)0); // Offset. 199 return; 200 } 201 202 Loc = SM.getSpellingLoc(Loc); 203 Record.push_back(SM.getSpellingLineNumber(Loc)); 204 Record.push_back(SM.getSpellingColumnNumber(Loc)); 205 206 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 207 FileID FID = LocInfo.first; 208 unsigned FileOffset = LocInfo.second; 209 Record.push_back(FileOffset); 210} 211 212void SDiagsWriter::EmitCharSourceRange(CharSourceRange R) { 213 Record.clear(); 214 Record.push_back(RECORD_SOURCE_RANGE); 215 AddLocToRecord(Diags.getSourceManager(), R.getBegin(), Record); 216 AddLocToRecord(Diags.getSourceManager(), R.getEnd(), Record); 217 Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_SOURCE_RANGE), Record); 218} 219 220/// \brief Emits the preamble of the diagnostics file. 221void SDiagsWriter::EmitPreamble() { 222 // EmitRawStringContents("CLANG_DIAGS"); 223 // Stream.Emit(Version, 32); 224 225 // Emit the file header. 226 Stream.Emit((unsigned)'D', 8); 227 Stream.Emit((unsigned) Version, 32 - 8); 228 229 EmitBlockInfoBlock(); 230} 231 232static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) { 233 using namespace llvm; 234 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line. 235 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column. 236 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset; 237} 238void SDiagsWriter::EmitBlockInfoBlock() { 239 Stream.EnterBlockInfoBlock(3); 240 241 // ==---------------------------------------------------------------------==// 242 // The subsequent records and Abbrevs are for the "Diagnostic" block. 243 // ==---------------------------------------------------------------------==// 244 245 EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record); 246 EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record); 247 EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record); 248 EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record); 249 EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record); 250 251 // Emit Abbrevs. 252 using namespace llvm; 253 254 // Emit abbreviation for RECORD_DIAG. 255 BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); 256 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG)); 257 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level. 258 AddSourceLocationAbbrev(Abbrev); 259 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category. 260 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. 261 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 262 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. 263 Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 264 265 // Emit abbrevation for RECORD_CATEGORY. 266 Abbrev = new BitCodeAbbrev(); 267 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); 268 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID. 269 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size. 270 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text. 271 Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 272 273 // Emit abbrevation for RECORD_SOURCE_RANGE. 274 Abbrev = new BitCodeAbbrev(); 275 Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE)); 276 AddSourceLocationAbbrev(Abbrev); 277 AddSourceLocationAbbrev(Abbrev); 278 Abbrevs.set(RECORD_SOURCE_RANGE, 279 Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 280 281 // Emit the abbreviation for RECORD_DIAG_FLAG. 282 Abbrev = new BitCodeAbbrev(); 283 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG)); 284 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. 285 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 286 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text. 287 Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, 288 Abbrev)); 289 290 // ==---------------------------------------------------------------------==// 291 // The subsequent records and Abbrevs are for the "Strings" block. 292 // ==---------------------------------------------------------------------==// 293 294 EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record); 295 EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record); 296 297 Abbrev = new BitCodeAbbrev(); 298 Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME)); 299 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Size. 300 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 64)); // Modifcation time. 301 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 302 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text. 303 Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS, 304 Abbrev)); 305 306 Stream.ExitBlock(); 307} 308 309unsigned SDiagsWriter::getEmitCategory(unsigned int DiagID) { 310 unsigned category = DiagnosticIDs::getCategoryNumberForDiag(DiagID); 311 312 if (Categories.count(category)) 313 return category; 314 315 Categories.insert(category); 316 317 // We use a local version of 'Record' so that we can be generating 318 // another record when we lazily generate one for the category entry. 319 RecordData Record; 320 Record.push_back(RECORD_CATEGORY); 321 Record.push_back(category); 322 StringRef catName = DiagnosticIDs::getCategoryNameFromID(category); 323 Record.push_back(catName.size()); 324 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName); 325 326 return category; 327} 328 329unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, 330 const Diagnostic &Info) { 331 if (DiagLevel == DiagnosticsEngine::Note) 332 return 0; // No flag for notes. 333 334 StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); 335 if (FlagName.empty()) 336 return 0; 337 338 // Here we assume that FlagName points to static data whose pointer 339 // value is fixed. This allows us to unique by diagnostic groups. 340 const void *data = FlagName.data(); 341 std::pair<unsigned, StringRef> &entry = DiagFlags[data]; 342 if (entry.first == 0) { 343 entry.first = DiagFlags.size(); 344 entry.second = FlagName; 345 346 // Lazily emit the string in a separate record. 347 RecordData Record; 348 Record.push_back(RECORD_DIAG_FLAG); 349 Record.push_back(entry.first); 350 Record.push_back(FlagName.size()); 351 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG), 352 Record, FlagName); 353 } 354 355 return entry.first; 356} 357 358void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) { 359 for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I) 360 Stream.Emit(*I, 8); 361} 362 363void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 364 const Diagnostic &Info) { 365 366 if (DiagLevel != DiagnosticsEngine::Note) { 367 if (inNonNoteDiagnostic) { 368 // We have encountered a non-note diagnostic. Finish up the previous 369 // diagnostic block before starting a new one. 370 Stream.ExitBlock(); 371 } 372 inNonNoteDiagnostic = true; 373 } 374 375 Stream.EnterSubblock(BLOCK_DIAG, 3); 376 377 // Emit the RECORD_DIAG record. 378 Record.clear(); 379 Record.push_back(RECORD_DIAG); 380 Record.push_back(DiagLevel); 381 AddLocToRecord(Diags.getSourceManager(), Info.getLocation(), Record); 382 // Emit the category string lazily and get the category ID. 383 Record.push_back(getEmitCategory(Info.getID())); 384 // Emit the diagnostic flag string lazily and get the mapped ID. 385 Record.push_back(getEmitDiagnosticFlag(DiagLevel, Info)); 386 387 388 diagBuf.clear(); 389 Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text. 390 Record.push_back(diagBuf.str().size()); 391 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str()); 392 393 ArrayRef<CharSourceRange> Ranges = Info.getRanges(); 394 for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end(); 395 it != ei; ++it) { 396 EmitCharSourceRange(*it); 397 } 398 399 // FIXME: emit fixits 400 401 if (DiagLevel == DiagnosticsEngine::Note) { 402 // Notes currently cannot have child diagnostics. Complete the 403 // diagnostic now. 404 Stream.ExitBlock(); 405 } 406} 407 408template <typename T> 409static void populateAndSort(std::vector<T> &scribble, 410 llvm::DenseSet<T> &set) { 411 scribble.clear(); 412 413 for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end(); 414 it != ei; ++it) 415 scribble.push_back(*it); 416 417 // Sort 'scribble' so we always have a deterministic ordering in the 418 // serialized file. 419 std::sort(scribble.begin(), scribble.end()); 420} 421 422void SDiagsWriter::EmitCategoriesAndFileNames() { 423 if (Categories.empty() && Files.empty()) 424 return; 425 426 BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS); 427 428 // Emit the file names. 429 { 430 std::vector<FileID> scribble; 431 populateAndSort(scribble, Files); 432 for (std::vector<FileID>::iterator it = scribble.begin(), 433 ei = scribble.end(); it != ei; ++it) { 434 SourceManager &SM = Diags.getSourceManager(); 435 const FileEntry *FE = SM.getFileEntryForID(*it); 436 StringRef Name = FE->getName(); 437 438 Record.clear(); 439 Record.push_back(FE->getSize()); 440 Record.push_back(FE->getModificationTime()); 441 Record.push_back(Name.size()); 442 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name); 443 } 444 } 445} 446 447void SDiagsWriter::EndSourceFile() { 448 if (inNonNoteDiagnostic) { 449 // Finish off any diagnostics we were in the process of emitting. 450 Stream.ExitBlock(); 451 inNonNoteDiagnostic = false; 452 } 453 454 EmitCategoriesAndFileNames(); 455 456 // Write the generated bitstream to "Out". 457 OS->write((char *)&Buffer.front(), Buffer.size()); 458 OS->flush(); 459 460 OS.reset(0); 461} 462 463