SerializedDiagnosticPrinter.cpp revision c820f183cd92d4a18de81b660a34f76e2cfdc463
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/Bitcode/BitstreamWriter.h" 12#include "llvm/Support/raw_ostream.h" 13#include "llvm/ADT/StringRef.h" 14#include "llvm/ADT/SmallString.h" 15#include "llvm/ADT/DenseSet.h" 16#include "clang/Basic/SourceManager.h" 17#include "clang/Basic/FileManager.h" 18#include "clang/Basic/Diagnostic.h" 19#include "clang/Basic/Version.h" 20#include "clang/Frontend/SerializedDiagnosticPrinter.h" 21 22using namespace clang; 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) 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 The version of the diagnostics file. 95 enum { Version = 1 }; 96 97 /// \brief The byte buffer for the serialized content. 98 std::vector<unsigned char> Buffer; 99 100 /// \brief The BitStreamWriter for the serialized diagnostics. 101 llvm::BitstreamWriter Stream; 102 103 /// \brief The name of the diagnostics file. 104 llvm::OwningPtr<llvm::raw_ostream> OS; 105 106 /// \brief The DiagnosticsEngine tied to all diagnostic locations. 107 DiagnosticsEngine &Diags; 108 109 /// \brief The set of constructed record abbreviations. 110 AbbreviationMap Abbrevs; 111 112 /// \brief A utility buffer for constructing record content. 113 RecordData Record; 114 115 /// \brief A text buffer for rendering diagnostic text. 116 llvm::SmallString<256> diagBuf; 117 118 /// \brief The collection of diagnostic categories used. 119 llvm::DenseSet<unsigned> Categories; 120 121 /// \brief The collection of files used. 122 llvm::DenseSet<FileID> Files; 123 124 enum BlockIDs { 125 /// \brief The DIAG block, which acts as a container around a diagnostic. 126 BLOCK_DIAG = llvm::bitc::FIRST_APPLICATION_BLOCKID, 127 /// \brief The STRINGS block, which contains strings 128 /// from multiple diagnostics. 129 BLOCK_STRINGS 130 }; 131 132 enum RecordIDs { 133 RECORD_DIAG = 1, 134 RECORD_DIAG_FLAG, 135 RECORD_CATEGORY, 136 RECORD_FILENAME 137 }; 138 139}; 140} // end anonymous namespace 141 142namespace clang { 143namespace serialized_diags { 144DiagnosticConsumer *create(llvm::raw_ostream *OS, DiagnosticsEngine &Diags) { 145 return new SDiagsWriter(Diags, OS); 146} 147} // end namespace serialized_diags 148} // end namespace clang 149 150//===----------------------------------------------------------------------===// 151// Serialization methods. 152//===----------------------------------------------------------------------===// 153 154/// \brief Emits a block ID in the BLOCKINFO block. 155static void EmitBlockID(unsigned ID, const char *Name, 156 llvm::BitstreamWriter &Stream, 157 RecordDataImpl &Record) { 158 Record.clear(); 159 Record.push_back(ID); 160 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record); 161 162 // Emit the block name if present. 163 if (Name == 0 || Name[0] == 0) 164 return; 165 166 Record.clear(); 167 168 while (*Name) 169 Record.push_back(*Name++); 170 171 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record); 172} 173 174/// \brief Emits a record ID in the BLOCKINFO block. 175static void EmitRecordID(unsigned ID, const char *Name, 176 llvm::BitstreamWriter &Stream, 177 RecordDataImpl &Record){ 178 Record.clear(); 179 Record.push_back(ID); 180 181 while (*Name) 182 Record.push_back(*Name++); 183 184 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); 185} 186 187/// \brief Emits the preamble of the diagnostics file. 188void SDiagsWriter::EmitPreamble() { 189 // EmitRawStringContents("CLANG_DIAGS"); 190 // Stream.Emit(Version, 32); 191 192 // Emit the file header. 193 Stream.Emit((unsigned)'D', 8); 194 Stream.Emit((unsigned)'I', 8); 195 Stream.Emit((unsigned)'A', 8); 196 Stream.Emit((unsigned)'G', 8); 197 198 EmitBlockInfoBlock(); 199} 200 201void SDiagsWriter::EmitBlockInfoBlock() { 202 Stream.EnterBlockInfoBlock(3); 203 204 // ==---------------------------------------------------------------------==// 205 // The subsequent records and Abbrevs are for the "Diagnostic" block. 206 // ==---------------------------------------------------------------------==// 207 208 EmitBlockID(BLOCK_DIAG, "Diagnostic", Stream, Record); 209 EmitRecordID(RECORD_DIAG, "Diagnostic Info", Stream, Record); 210 EmitRecordID(RECORD_DIAG_FLAG, "Diagnostic Flag", Stream, Record); 211 212 // Emit Abbrevs. 213 using namespace llvm; 214 215 // Emit abbreviation for RECORD_DIAG. 216 BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); 217 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG)); 218 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level. 219 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16-3)); // Category. 220 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 221 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. 222 Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 223 224 225 // Emit the abbreviation for RECORD_DIAG_FLAG. 226 Abbrev = new BitCodeAbbrev(); 227 Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG)); 228 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 229 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text. 230 Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); 231 232 // ==---------------------------------------------------------------------==// 233 // The subsequent records and Abbrevs are for the "Strings" block. 234 // ==---------------------------------------------------------------------==// 235 236 EmitBlockID(BLOCK_STRINGS, "Strings", Stream, Record); 237 EmitRecordID(RECORD_CATEGORY, "Category Name", Stream, Record); 238 EmitRecordID(RECORD_FILENAME, "File Name", Stream, Record); 239 240 Abbrev = new BitCodeAbbrev(); 241 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); 242 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size. 243 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text. 244 Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS, 245 Abbrev)); 246 247 Abbrev = new BitCodeAbbrev(); 248 Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); 249 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. 250 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text. 251 Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_STRINGS, 252 Abbrev)); 253 254 Stream.ExitBlock(); 255} 256 257void SDiagsWriter::EmitRawStringContents(llvm::StringRef str) { 258 for (StringRef::const_iterator I = str.begin(), E = str.end(); I!=E; ++I) 259 Stream.Emit(*I, 8); 260} 261 262void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 263 const Diagnostic &Info) { 264 265 BlockEnterExit DiagBlock(Stream, BLOCK_DIAG); 266 267 // Emit the RECORD_DIAG record. 268 Record.clear(); 269 Record.push_back(RECORD_DIAG); 270 Record.push_back(DiagLevel); 271 unsigned category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); 272 Record.push_back(category); 273 Categories.insert(category); 274 diagBuf.clear(); 275 Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text. 276 Record.push_back(diagBuf.str().size()); 277 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str()); 278 279 // Emit the RECORD_DIAG_FLAG record. 280 StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); 281 if (!FlagName.empty()) { 282 Record.clear(); 283 Record.push_back(RECORD_DIAG_FLAG); 284 Record.push_back(FlagName.size()); 285 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG), 286 Record, FlagName.str()); 287 } 288 289 // FIXME: emit location 290 // FIXME: emit ranges 291 // FIXME: emit notes 292 // FIXME: emit fixits 293} 294 295template <typename T> 296static void populateAndSort(std::vector<T> &scribble, 297 llvm::DenseSet<T> &set) { 298 scribble.clear(); 299 300 for (typename llvm::DenseSet<T>::iterator it = set.begin(), ei = set.end(); 301 it != ei; ++it) 302 scribble.push_back(*it); 303 304 // Sort 'scribble' so we always have a deterministic ordering in the 305 // serialized file. 306 std::sort(scribble.begin(), scribble.end()); 307} 308 309void SDiagsWriter::EmitCategoriesAndFileNames() { 310 311 if (Categories.empty() && Files.empty()) 312 return; 313 314 BlockEnterExit BlockEnter(Stream, BLOCK_STRINGS); 315 316 // Emit the category names. 317 { 318 std::vector<unsigned> scribble; 319 populateAndSort(scribble, Categories); 320 for (std::vector<unsigned>::iterator it = scribble.begin(), 321 ei = scribble.end(); it != ei ; ++it) { 322 Record.clear(); 323 Record.push_back(RECORD_CATEGORY); 324 StringRef catName = DiagnosticIDs::getCategoryNameFromID(*it); 325 Record.push_back(catName.size()); 326 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName); 327 } 328 } 329 330 // Emit the file names. 331 { 332 std::vector<FileID> scribble; 333 populateAndSort(scribble, Files); 334 for (std::vector<FileID>::iterator it = scribble.begin(), 335 ei = scribble.end(); it != ei; ++it) { 336 SourceManager &SM = Diags.getSourceManager(); 337 const FileEntry *FE = SM.getFileEntryForID(*it); 338 StringRef Name = FE->getName(); 339 340 Record.clear(); 341 Record.push_back(Name.size()); 342 Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name); 343 } 344 } 345 346} 347 348void SDiagsWriter::EndSourceFile() { 349 EmitCategoriesAndFileNames(); 350 351 // Write the generated bitstream to "Out". 352 OS->write((char *)&Buffer.front(), Buffer.size()); 353 OS->flush(); 354 355 OS.reset(0); 356} 357 358