diff options
Diffstat (limited to 'lib/Frontend/SerializedDiagnosticPrinter.cpp')
-rw-r--r-- | lib/Frontend/SerializedDiagnosticPrinter.cpp | 257 |
1 files changed, 234 insertions, 23 deletions
diff --git a/lib/Frontend/SerializedDiagnosticPrinter.cpp b/lib/Frontend/SerializedDiagnosticPrinter.cpp index 29c58a8..f701f72 100644 --- a/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -14,6 +14,10 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" #include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/SerializedDiagnosticReader.h" +#include "clang/Frontend/SerializedDiagnostics.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallString.h" @@ -86,20 +90,70 @@ protected: void endDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level) override; }; - + +typedef llvm::DenseMap<unsigned, unsigned> AbbrevLookup; + +class SDiagsMerger : SerializedDiagnosticReader { + SDiagsWriter &Writer; + AbbrevLookup FileLookup; + AbbrevLookup CategoryLookup; + AbbrevLookup DiagFlagLookup; + +public: + SDiagsMerger(SDiagsWriter &Writer) + : SerializedDiagnosticReader(), Writer(Writer) {} + + std::error_code mergeRecordsFromFile(const char *File) { + return readDiagnostics(File); + } + +protected: + std::error_code visitStartOfDiagnostic() override; + std::error_code visitEndOfDiagnostic() override; + std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override; + std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override; + std::error_code visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) override; + std::error_code visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) override; + std::error_code visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef CodeToInsert) override; + std::error_code + visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) override; + +private: + std::error_code adjustSourceLocFilename(RecordData &Record, + unsigned int offset); + + void adjustAbbrevID(RecordData &Record, AbbrevLookup &Lookup, + unsigned NewAbbrev); + + void writeRecordWithAbbrev(unsigned ID, RecordData &Record); + + void writeRecordWithBlob(unsigned ID, RecordData &Record, StringRef Blob); +}; + class SDiagsWriter : public DiagnosticConsumer { friend class SDiagsRenderer; + friend class SDiagsMerger; struct SharedState; explicit SDiagsWriter(IntrusiveRefCntPtr<SharedState> State) - : LangOpts(nullptr), OriginalInstance(false), State(State) {} + : LangOpts(nullptr), OriginalInstance(false), MergeChildRecords(false), + State(State) {} public: - SDiagsWriter(raw_ostream *os, DiagnosticOptions *diags) - : LangOpts(nullptr), OriginalInstance(true), - State(new SharedState(os, diags)) - { + SDiagsWriter(StringRef File, DiagnosticOptions *Diags, bool MergeChildRecords) + : LangOpts(nullptr), OriginalInstance(true), + MergeChildRecords(MergeChildRecords), + State(new SharedState(File, Diags)) { + if (MergeChildRecords) + RemoveOldDiagnostics(); EmitPreamble(); } @@ -115,6 +169,14 @@ public: void finish() override; private: + /// \brief Build a DiagnosticsEngine to emit diagnostics about the diagnostics + DiagnosticsEngine *getMetaDiags(); + + /// \brief Remove old copies of the serialized diagnostics. This is necessary + /// so that we can detect when subprocesses write diagnostics that we should + /// merge into our own. + void RemoveOldDiagnostics(); + /// \brief Emit the preamble for the serialized diagnostics. void EmitPreamble(); @@ -152,7 +214,9 @@ private: /// \brief Emit the string information for diagnostic flags. unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, unsigned DiagID = 0); - + + unsigned getEmitDiagnosticFlag(StringRef DiagName); + /// \brief Emit (lazily) the file string and retrieved the file identifier. unsigned getEmitFile(const char *Filename); @@ -173,9 +237,6 @@ private: void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record, const SourceManager &SM); - /// \brief The version of the diagnostics file. - enum { Version = 2 }; - /// \brief Language options, which can differ from one clone of this client /// to another. const LangOptions *LangOpts; @@ -184,11 +245,16 @@ private: /// clones), responsible for writing the file at the end. bool OriginalInstance; + /// \brief Whether this instance should aggregate diagnostics that are + /// generated from child processes. + bool MergeChildRecords; + /// \brief State that is shared among the various clones of this diagnostic /// consumer. struct SharedState : RefCountedBase<SharedState> { - SharedState(raw_ostream *os, DiagnosticOptions *diags) - : DiagOpts(diags), Stream(Buffer), OS(os), EmittedAnyDiagBlocks(false) { } + SharedState(StringRef File, DiagnosticOptions *Diags) + : DiagOpts(Diags), Stream(Buffer), OutputFile(File.str()), + EmittedAnyDiagBlocks(false) {} /// \brief Diagnostic options. IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; @@ -200,7 +266,7 @@ private: llvm::BitstreamWriter Stream; /// \brief The name of the diagnostics file. - std::unique_ptr<raw_ostream> OS; + std::string OutputFile; /// \brief The set of constructed record abbreviations. AbbreviationMap Abbrevs; @@ -227,6 +293,9 @@ private: /// this becomes \c true, we never close a DIAG block until we know that we're /// starting another one or we're done. bool EmittedAnyDiagBlocks; + + /// \brief Engine for emitting diagnostics about the diagnostics. + std::unique_ptr<DiagnosticsEngine> MetaDiagnostics; }; /// \brief State shared among the various clones of this diagnostic consumer. @@ -236,9 +305,11 @@ private: namespace clang { namespace serialized_diags { -DiagnosticConsumer *create(raw_ostream *OS, DiagnosticOptions *diags) { - return new SDiagsWriter(OS, diags); +std::unique_ptr<DiagnosticConsumer> +create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords) { + return llvm::make_unique<SDiagsWriter>(OutputFile, Diags, MergeChildRecords); } + } // end namespace serialized_diags } // end namespace clang @@ -465,17 +536,15 @@ void SDiagsWriter::EmitMetaBlock() { Stream.EnterSubblock(BLOCK_META, 3); Record.clear(); Record.push_back(RECORD_VERSION); - Record.push_back(Version); + Record.push_back(VersionNumber); Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record); Stream.ExitBlock(); } unsigned SDiagsWriter::getEmitCategory(unsigned int category) { - if (State->Categories.count(category)) + if (!State->Categories.insert(category).second) return category; - - State->Categories.insert(category); - + // We use a local version of 'Record' so that we can be generating // another record when we lazily generate one for the category entry. RecordData Record; @@ -495,6 +564,10 @@ unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, return 0; // No flag for notes. StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(DiagID); + return getEmitDiagnosticFlag(FlagName); +} + +unsigned SDiagsWriter::getEmitDiagnosticFlag(StringRef FlagName) { if (FlagName.empty()) return 0; @@ -689,6 +762,40 @@ void SDiagsRenderer::emitNote(SourceLocation Loc, StringRef Message, Writer.ExitDiagBlock(); } +DiagnosticsEngine *SDiagsWriter::getMetaDiags() { + // FIXME: It's slightly absurd to create a new diagnostics engine here, but + // the other options that are available today are worse: + // + // 1. Teach DiagnosticsConsumers to emit diagnostics to the engine they are a + // part of. The DiagnosticsEngine would need to know not to send + // diagnostics back to the consumer that failed. This would require us to + // rework ChainedDiagnosticsConsumer and teach the engine about multiple + // consumers, which is difficult today because most APIs interface with + // consumers rather than the engine itself. + // + // 2. Pass a DiagnosticsEngine to SDiagsWriter on creation - this would need + // to be distinct from the engine the writer was being added to and would + // normally not be used. + if (!State->MetaDiagnostics) { + IntrusiveRefCntPtr<DiagnosticIDs> IDs(new DiagnosticIDs()); + auto Client = + new TextDiagnosticPrinter(llvm::errs(), State->DiagOpts.get()); + State->MetaDiagnostics = llvm::make_unique<DiagnosticsEngine>( + IDs, State->DiagOpts.get(), Client); + } + return State->MetaDiagnostics.get(); +} + +void SDiagsWriter::RemoveOldDiagnostics() { + if (!llvm::sys::fs::remove(State->OutputFile)) + return; + + getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); + // Disable merging child records, as whatever is in this file may be + // misleading. + MergeChildRecords = false; +} + void SDiagsWriter::finish() { // The original instance is responsible for writing the file. if (!OriginalInstance) @@ -698,9 +805,113 @@ void SDiagsWriter::finish() { if (State->EmittedAnyDiagBlocks) ExitDiagBlock(); + if (MergeChildRecords) { + if (!State->EmittedAnyDiagBlocks) + // We have no diagnostics of our own, so we can just leave the child + // process' output alone + return; + + if (llvm::sys::fs::exists(State->OutputFile)) + if (SDiagsMerger(*this).mergeRecordsFromFile(State->OutputFile.c_str())) + getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); + } + + std::error_code EC; + auto OS = llvm::make_unique<llvm::raw_fd_ostream>(State->OutputFile.c_str(), + EC, llvm::sys::fs::F_None); + if (EC) { + getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure) + << State->OutputFile << EC.message(); + return; + } + // Write the generated bitstream to "Out". - State->OS->write((char *)&State->Buffer.front(), State->Buffer.size()); - State->OS->flush(); + OS->write((char *)&State->Buffer.front(), State->Buffer.size()); + OS->flush(); +} + +std::error_code SDiagsMerger::visitStartOfDiagnostic() { + Writer.EnterDiagBlock(); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitEndOfDiagnostic() { + Writer.ExitDiagBlock(); + return std::error_code(); +} + +std::error_code +SDiagsMerger::visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) { + RecordData Record; + Record.push_back(RECORD_SOURCE_RANGE); + Record.push_back(FileLookup[Start.FileID]); + Record.push_back(Start.Line); + Record.push_back(Start.Col); + Record.push_back(Start.Offset); + Record.push_back(FileLookup[End.FileID]); + Record.push_back(End.Line); + Record.push_back(End.Col); + Record.push_back(End.Offset); + + Writer.State->Stream.EmitRecordWithAbbrev( + Writer.State->Abbrevs.get(RECORD_SOURCE_RANGE), Record); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) { + RecordData MergedRecord; + MergedRecord.push_back(RECORD_DIAG); + MergedRecord.push_back(Severity); + MergedRecord.push_back(FileLookup[Location.FileID]); + MergedRecord.push_back(Location.Line); + MergedRecord.push_back(Location.Col); + MergedRecord.push_back(Location.Offset); + MergedRecord.push_back(CategoryLookup[Category]); + MergedRecord.push_back(Flag ? DiagFlagLookup[Flag] : 0); + MergedRecord.push_back(Message.size()); + + Writer.State->Stream.EmitRecordWithBlob( + Writer.State->Abbrevs.get(RECORD_DIAG), MergedRecord, Message); + return std::error_code(); +} + +std::error_code +SDiagsMerger::visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef Text) { + RecordData Record; + Record.push_back(RECORD_FIXIT); + Record.push_back(FileLookup[Start.FileID]); + Record.push_back(Start.Line); + Record.push_back(Start.Col); + Record.push_back(Start.Offset); + Record.push_back(FileLookup[End.FileID]); + Record.push_back(End.Line); + Record.push_back(End.Col); + Record.push_back(End.Offset); + Record.push_back(Text.size()); + + Writer.State->Stream.EmitRecordWithBlob( + Writer.State->Abbrevs.get(RECORD_FIXIT), Record, Text); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) { + FileLookup[ID] = Writer.getEmitFile(Name.str().c_str()); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitCategoryRecord(unsigned ID, StringRef Name) { + CategoryLookup[ID] = Writer.getEmitCategory(ID); + return std::error_code(); +} - State->OS.reset(); +std::error_code SDiagsMerger::visitDiagFlagRecord(unsigned ID, StringRef Name) { + DiagFlagLookup[ID] = Writer.getEmitDiagnosticFlag(Name); + return std::error_code(); } |