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