Skip to content

Commit 762b106

Browse files
[CAS] Add caching diagnostic processor
Add a CachedDiagnosticsProcessor that is a DiagConsumer can capture all the diagnostics during a compilation, serialized them into CAS with a format that can be replayed without re-compiling.
1 parent bfe975d commit 762b106

9 files changed

+996
-18
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,9 @@ ERROR(error_create_cas, none, "failed to create CAS '%0' (%1)", (StringRef, Stri
494494
ERROR(error_invalid_cas_id, none, "invalid CASID '%0' (%1)", (StringRef, StringRef))
495495
ERROR(error_cas, none, "CAS error encountered: %0", (StringRef))
496496

497+
ERROR(error_failed_cached_diag, none, "failed to serialize cached diagnostics: %0", (StringRef))
498+
ERROR(error_replay_cached_diag, none, "failed to replay cached diagnostics: %0", (StringRef))
499+
497500
// Dependency Verifier Diagnostics
498501
ERROR(missing_member_dependency,none,
499502
"expected "
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===--- CachedDiagnostics.h - Cached Diagnostics ---------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines the CachedDiagnosticConsumer class, which
14+
// caches the diagnostics which can be replayed with other DiagnosticConumers.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_CACHEDDIAGNOSTICS_H
19+
#define SWIFT_CACHEDDIAGNOSTICS_H
20+
21+
#include "llvm/Support/Error.h"
22+
23+
namespace swift {
24+
25+
class CompilerInstance;
26+
class DiagnosticEngine;
27+
class SourceManager;
28+
class FrontendInputsAndOutputs;
29+
30+
class CachingDiagnosticsProcessor {
31+
public:
32+
CachingDiagnosticsProcessor(CompilerInstance &Instance);
33+
~CachingDiagnosticsProcessor();
34+
35+
/// Start capturing all the diagnostics from DiagnosticsEngine.
36+
void startCaptureDiagnostics();
37+
/// End capturing all the diagnostics from DiagnosticsEngine.
38+
void endCaptureDiagnostics();
39+
40+
/// Disable catpure.
41+
void setDisableCapture();
42+
43+
/// Emit serialized diagnostics into output stream.
44+
llvm::Error serializeEmittedDiagnostics(llvm::raw_ostream &os);
45+
46+
/// Used to replay the previously cached diagnostics, after a cache hit.
47+
llvm::Error replayCachedDiagnostics(llvm::StringRef Buffer);
48+
49+
private:
50+
class Implementation;
51+
Implementation& Impl;
52+
};
53+
54+
}
55+
56+
#endif

include/swift/Frontend/Frontend.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/Basic/LangOptions.h"
3131
#include "swift/Basic/SourceManager.h"
3232
#include "swift/ClangImporter/ClangImporter.h"
33+
#include "swift/Frontend/CachedDiagnostics.h"
3334
#include "swift/Frontend/DiagnosticVerifier.h"
3435
#include "swift/Frontend/FrontendOptions.h"
3536
#include "swift/Frontend/ModuleInterfaceSupport.h"
@@ -462,6 +463,7 @@ class CompilerInstance {
462463
std::unique_ptr<ASTContext> Context;
463464
std::unique_ptr<Lowering::TypeConverter> TheSILTypes;
464465
std::unique_ptr<DiagnosticVerifier> DiagVerifier;
466+
std::unique_ptr<CachingDiagnosticsProcessor> CDP;
465467

466468
/// A cache describing the set of inter-module dependencies that have been queried.
467469
/// Null if not present.
@@ -548,6 +550,9 @@ class CompilerInstance {
548550
Optional<llvm::cas::ObjectRef> getCompilerBaseKey() const {
549551
return CompileJobBaseKey;
550552
}
553+
CachingDiagnosticsProcessor *getCachingDiagnosticsProcessor() const {
554+
return CDP.get();
555+
}
551556

552557
ASTContext &getASTContext() { return *Context; }
553558
const ASTContext &getASTContext() const { return *Context; }
@@ -677,6 +682,7 @@ class CompilerInstance {
677682
void setupDependencyTrackerIfNeeded();
678683
bool setupCASIfNeeded(ArrayRef<const char *> Args);
679684
void setupOutputBackend();
685+
void setupCachingDiagnosticsProcessorIfNeeded();
680686

681687
/// \return false if successful, true on error.
682688
bool setupDiagnosticVerifierIfNeeded();

lib/DriverTool/swift_cache_tool_main.cpp

Lines changed: 87 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ enum class SwiftCacheToolAction {
4242
Invalid,
4343
PrintBaseKey,
4444
PrintOutputKeys,
45-
ValidateOutputs
45+
ValidateOutputs,
46+
RenderDiags
4647
};
4748

4849
struct OutputEntry {
@@ -134,11 +135,13 @@ class SwiftCacheToolInvocation {
134135
.Case("print-base-key", SwiftCacheToolAction::PrintBaseKey)
135136
.Case("print-output-keys", SwiftCacheToolAction::PrintOutputKeys)
136137
.Case("validate-outputs", SwiftCacheToolAction::ValidateOutputs)
138+
.Case("render-diags", SwiftCacheToolAction::RenderDiags)
137139
.Default(SwiftCacheToolAction::Invalid);
138140

139141
if (ActionKind == SwiftCacheToolAction::Invalid) {
140-
llvm::errs() << "Invalid option specified for -cache-tool-action: "
141-
<< "use print-base-key|print-output-keys|validate-outputs\n";
142+
llvm::errs()
143+
<< "Invalid option specified for -cache-tool-action: "
144+
<< "print-base-key|print-output-keys|validate-outputs|render-diags\n";
142145
return 1;
143146
}
144147

@@ -153,6 +156,8 @@ class SwiftCacheToolInvocation {
153156
return printOutputKeys();
154157
case SwiftCacheToolAction::ValidateOutputs:
155158
return validateOutputs();
159+
case SwiftCacheToolAction::RenderDiags:
160+
return renderDiags();
156161
case SwiftCacheToolAction::Invalid:
157162
return 0; // No action. Probably just print help. Return.
158163
}
@@ -202,6 +207,12 @@ class SwiftCacheToolInvocation {
202207
return true;
203208
}
204209

210+
// Disable diagnostic caching from this fake instance.
211+
if (auto *CDP = Instance.getCachingDiagnosticsProcessor()) {
212+
CDP->endCaptureDiagnostics();
213+
CDP->setDisableCapture();
214+
}
215+
205216
return false;
206217
}
207218

@@ -233,6 +244,7 @@ class SwiftCacheToolInvocation {
233244

234245
int printOutputKeys();
235246
int validateOutputs();
247+
int renderDiags();
236248
};
237249

238250
} // end anonymous namespace
@@ -283,6 +295,10 @@ int SwiftCacheToolInvocation::printOutputKeys() {
283295
Invocation.getFrontendOptions().InputsAndOutputs.getAllInputs(),
284296
addFromInputFile);
285297

298+
// Add diagnostics file.
299+
addOutputKey("<cached-diagnostics>", file_types::ID::TY_CachedDiagnostics,
300+
"<cached-diagnostics>");
301+
286302
if (hasError)
287303
return 1;
288304

@@ -301,6 +317,26 @@ int SwiftCacheToolInvocation::printOutputKeys() {
301317
return 0;
302318
}
303319

320+
static llvm::Expected<llvm::json::Array>
321+
readOutputEntriesFromFile(StringRef Path) {
322+
auto JSONContent = llvm::MemoryBuffer::getFile(Path);
323+
if (!JSONContent)
324+
return llvm::createStringError(JSONContent.getError(),
325+
"failed to read input file");
326+
327+
auto JSONValue = llvm::json::parse((*JSONContent)->getBuffer());
328+
if (!JSONValue)
329+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
330+
"failed to parse input file as JSON");
331+
332+
auto Keys = JSONValue->getAsArray();
333+
if (!Keys)
334+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
335+
"invalid JSON format for input file");
336+
337+
return *Keys;
338+
}
339+
304340
int SwiftCacheToolInvocation::validateOutputs() {
305341
auto DB = llvm::cas::createOnDiskUnifiedCASDatabases(CASPath);
306342
if (!DB)
@@ -310,22 +346,10 @@ int SwiftCacheToolInvocation::validateOutputs() {
310346
Instance.getDiags().addConsumer(PDC);
311347

312348
auto validateCacheKeysFromFile = [&](const std::string &Path) {
313-
auto JSONContent = llvm::MemoryBuffer::getFile(Path);
314-
if (!JSONContent) {
315-
llvm::errs() << "failed to read " << Path << ": "
316-
<< JSONContent.getError().message() << "\n";
317-
return true;
318-
}
319-
auto JSONValue = llvm::json::parse((*JSONContent)->getBuffer());
320-
if (!JSONValue) {
321-
llvm::errs() << "failed to parse " << Path << ": "
322-
<< toString(JSONValue.takeError()) << "\n";
323-
return true;
324-
}
325-
326-
auto Keys = JSONValue->getAsArray();
349+
auto Keys = readOutputEntriesFromFile(Path);
327350
if (!Keys) {
328-
llvm::errs() << "invalid keys format in " << Path << "\n";
351+
llvm::errs() << "cannot read file " << Path << ": "
352+
<< toString(Keys.takeError()) << "\n";
329353
return true;
330354
}
331355

@@ -350,6 +374,51 @@ int SwiftCacheToolInvocation::validateOutputs() {
350374
return llvm::any_of(Inputs, validateCacheKeysFromFile);
351375
}
352376

377+
int SwiftCacheToolInvocation::renderDiags() {
378+
if (setupCompiler())
379+
return 1;
380+
381+
auto *CDP = Instance.getCachingDiagnosticsProcessor();
382+
if (!CDP) {
383+
llvm::errs() << "provided commandline doesn't support cached diagnostics\n";
384+
return 1;
385+
}
386+
387+
auto renderDiagsFromFile = [&](const std::string &Path) {
388+
auto Keys = readOutputEntriesFromFile(Path);
389+
if (!Keys) {
390+
llvm::errs() << "cannot read file " << Path << ": "
391+
<< toString(Keys.takeError()) << "\n";
392+
return true;
393+
}
394+
395+
for (const auto& Entry : *Keys) {
396+
if (auto *Obj = Entry.getAsObject()) {
397+
if (auto Kind = Obj->getString("OutputKind")) {
398+
if (*Kind != "cached-diagnostics")
399+
continue;
400+
}
401+
if (auto Key = Obj->getString("CacheKey")) {
402+
if (auto Buffer = loadCachedCompileResultFromCacheKey(
403+
Instance.getObjectStore(), Instance.getActionCache(),
404+
Instance.getDiags(), *Key)) {
405+
if (auto E = CDP->replayCachedDiagnostics(Buffer->getBuffer())) {
406+
llvm::errs() << "failed to replay cache: "
407+
<< toString(std::move(E)) << "\n";
408+
return true;
409+
}
410+
return false;
411+
}
412+
}
413+
}
414+
}
415+
llvm::errs() << "cannot locate cached diagnostics in file\n";
416+
return true;
417+
};
418+
419+
return llvm::any_of(Inputs, renderDiagsFromFile);
420+
}
421+
353422
int swift_cache_tool_main(ArrayRef<const char *> Args, const char *Argv0,
354423
void *MainAddr) {
355424
INITIALIZE_LLVM();

lib/Frontend/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ add_swift_host_library(swiftFrontend STATIC
33
ArgsToFrontendInputsConverter.cpp
44
ArgsToFrontendOptionsConverter.cpp
55
ArgsToFrontendOutputsConverter.cpp
6+
CachedDiagnostics.cpp
67
CachingUtils.cpp
78
CompileJobCacheKey.cpp
89
CompilerInvocation.cpp

0 commit comments

Comments
 (0)