aboutsummaryrefslogtreecommitdiff
path: root/lib/Frontend/SerializedDiagnosticPrinter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Frontend/SerializedDiagnosticPrinter.cpp')
-rw-r--r--lib/Frontend/SerializedDiagnosticPrinter.cpp257
1 files changed, 234 insertions, 23 deletions
diff --git a/lib/Frontend/SerializedDiagnosticPrinter.cpp b/lib/Frontend/SerializedDiagnosticPrinter.cpp
index 29c58a8ac34e..f701f723a320 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();
}