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