Skip to content

Commit c820485

Browse files
committed
[Dependency Scanning] Configure a thread-safe serialized diagnostics consumer for in-memory scans
This change introduces a thread-safe version of the 'SerializedDiagnosticConsumer' and refactors scanning compilation instance creation code to ensure this consumer gets added when the scanner query configuration command-line includes '-serialized-diagnostics-path' option.
1 parent 56a525b commit c820485

File tree

8 files changed

+403
-207
lines changed

8 files changed

+403
-207
lines changed

include/swift/DependencyScan/DependencyScanningTool.h

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,34 @@
1515

1616
#include "swift-c/DependencyScan/DependencyScan.h"
1717
#include "swift/Frontend/Frontend.h"
18+
#include "swift/AST/DiagnosticConsumer.h"
1819
#include "swift/AST/ModuleDependencies.h"
1920
#include "swift/DependencyScan/ScanDependencies.h"
2021
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
22+
#include "swift/Frontend/SerializedDiagnosticConsumer.h"
2123
#include "llvm/Support/Error.h"
2224
#include "llvm/Support/StringSaver.h"
2325

2426
namespace swift {
2527
namespace dependencies {
2628
class DependencyScanningTool;
27-
class DependencyScanDiagnosticCollector;
28-
29-
struct ScanQueryInstance {
30-
std::unique_ptr<CompilerInstance> ScanInstance;
31-
std::shared_ptr<DependencyScanDiagnosticCollector> ScanDiagnostics;
29+
class DepScanInMemoryDiagnosticCollector;
30+
31+
struct ScanQueryContext {
32+
/// Primary CompilerInstance configured for this scanning action
33+
llvm::ErrorOr<std::unique_ptr<CompilerInstance>> ScanInstance;
34+
/// An thread-safe diagnostic consumer which collects all emitted
35+
/// diagnostics in the scan to be reporte via libSwiftScan API
36+
std::unique_ptr<DepScanInMemoryDiagnosticCollector> InMemoryDiagnosticCollector;
37+
/// A thread-safe serialized diagnostics consumer.
38+
/// Note, although type-erased, this must be an instance of
39+
/// 'ThreadSafeSerializedDiagnosticConsumer'
40+
std::unique_ptr<DiagnosticConsumer> SerializedDiagnosticConsumer;
41+
42+
~ScanQueryContext() {
43+
if (SerializedDiagnosticConsumer)
44+
SerializedDiagnosticConsumer->finishProcessing();
45+
}
3246
};
3347

3448
/// Pure virtual Diagnostic consumer intended for collecting
@@ -47,12 +61,16 @@ class ThreadSafeDiagnosticCollector : public DiagnosticConsumer {
4761
};
4862

4963
/// Diagnostic consumer that simply collects the diagnostics emitted so-far
50-
class DependencyScanDiagnosticCollector : public ThreadSafeDiagnosticCollector {
64+
/// and uses a representation agnostic from any specific CompilerInstance state
65+
/// which may have been used to emit the diagnostic
66+
class DepScanInMemoryDiagnosticCollector
67+
: public ThreadSafeDiagnosticCollector {
5168
private:
5269
struct ScannerDiagnosticInfo {
5370
std::string Message;
5471
llvm::SourceMgr::DiagKind Severity;
55-
std::optional<ScannerImportStatementInfo::ImportDiagnosticLocationInfo> ImportLocation;
72+
std::optional<ScannerImportStatementInfo::ImportDiagnosticLocationInfo>
73+
ImportLocation;
5674
};
5775
std::vector<ScannerDiagnosticInfo> Diagnostics;
5876

@@ -61,7 +79,7 @@ class DependencyScanDiagnosticCollector : public ThreadSafeDiagnosticCollector {
6179

6280
public:
6381
friend DependencyScanningTool;
64-
DependencyScanDiagnosticCollector() {}
82+
DepScanInMemoryDiagnosticCollector() {}
6583
void reset() { Diagnostics.clear(); }
6684
const std::vector<ScannerDiagnosticInfo> &getDiagnostics() const {
6785
return Diagnostics;
@@ -97,10 +115,9 @@ class DependencyScanningTool {
97115

98116
/// Using the specified invocation command, instantiate a CompilerInstance
99117
/// that will be used for this scan.
100-
llvm::ErrorOr<ScanQueryInstance>
101-
initCompilerInstanceForScan(ArrayRef<const char *> Command,
102-
StringRef WorkingDirectory,
103-
std::shared_ptr<DependencyScanDiagnosticCollector> scannerDiagnosticsCollector);
118+
ScanQueryContext
119+
createScanQueryContext(ArrayRef<const char *> Command,
120+
StringRef WorkingDirectory);
104121

105122
private:
106123
/// Shared cache of module dependencies, re-used by individual full-scan queries
@@ -113,7 +130,7 @@ class DependencyScanningTool {
113130
llvm::StringSaver Saver;
114131
};
115132

116-
swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput(const DependencyScanDiagnosticCollector *diagnosticCollector);
133+
swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput(const DepScanInMemoryDiagnosticCollector *diagnosticCollector);
117134

118135
} // end namespace dependencies
119136
} // end namespace swift

include/swift/DependencyScan/ScanDependencies.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ using ModuleDependencyIDSet =
4343
class SwiftDependencyScanningService;
4444

4545
namespace dependencies {
46-
class DependencyScanDiagnosticCollector;
46+
struct ScanQueryContext;
4747

4848
using CompilerArgInstanceCacheMap =
4949
llvm::StringMap<std::tuple<std::unique_ptr<CompilerInstance>,
@@ -62,16 +62,15 @@ bool prescanDependencies(CompilerInstance &instance);
6262
/// Scans the dependencies of the main module of \c instance.
6363
llvm::ErrorOr<swiftscan_dependency_graph_t>
6464
performModuleScan(SwiftDependencyScanningService &service,
65-
CompilerInstance &instance,
6665
ModuleDependenciesCache &cache,
67-
DependencyScanDiagnosticCollector *diagnostics = nullptr);
66+
ScanQueryContext &queryContext);
6867

6968
/// Scans the main module of \c instance for all direct module imports
7069
llvm::ErrorOr<swiftscan_import_set_t>
7170
performModulePrescan(SwiftDependencyScanningService &service,
72-
CompilerInstance &instance,
7371
ModuleDependenciesCache &cache,
74-
DependencyScanDiagnosticCollector *diagnostics = nullptr);
72+
ScanQueryContext &queryContext);
73+
7574

7675
namespace incremental {
7776
/// For the given module dependency graph captured in the 'cache',

include/swift/Frontend/SerializedDiagnosticConsumer.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ namespace swift {
3737
/// \returns A new diagnostic consumer that serializes diagnostics.
3838
std::unique_ptr<DiagnosticConsumer>
3939
createConsumer(llvm::StringRef outputPath, bool emitMacroExpansionFiles);
40+
41+
/// Create a thread-safe DiagnosticConsumer that serializes diagnostics to a file,
42+
/// using Clang's serialized diagnostics format.
43+
///
44+
/// \param outputPath the file path to write the diagnostics to.
45+
///
46+
/// \returns A new diagnostic consumer that serializes diagnostics.
47+
std::unique_ptr<DiagnosticConsumer>
48+
createThreadSafeConsumer(llvm::StringRef outputPath, bool emitMacroExpansionFiles);
4049
}
4150
}
4251

lib/DependencyScan/DependencyScanningTool.cpp

Lines changed: 63 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void ThreadSafeDiagnosticCollector::handleDiagnostic(SourceManager &SM,
7373
}
7474
}
7575

76-
void DependencyScanDiagnosticCollector::addDiagnostic(
76+
void DepScanInMemoryDiagnosticCollector::addDiagnostic(
7777
SourceManager &SM, const DiagnosticInfo &Info) {
7878
// Determine what kind of diagnostic we're emitting.
7979
llvm::SourceMgr::DiagKind SMKind;
@@ -131,7 +131,7 @@ void DependencyScanDiagnosticCollector::addDiagnostic(
131131
}
132132

133133
swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput(
134-
const DependencyScanDiagnosticCollector *diagnosticCollector) {
134+
const DepScanInMemoryDiagnosticCollector *diagnosticCollector) {
135135
auto collectedDiagnostics = diagnosticCollector->getDiagnostics();
136136
auto numDiagnostics = collectedDiagnostics.size();
137137
swiftscan_diagnostic_set_t *diagnosticOutput = new swiftscan_diagnostic_set_t;
@@ -183,7 +183,7 @@ swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput(
183183
// module dependnecies but captures the diagnostics emitted during the attempted
184184
// scan query.
185185
static swiftscan_dependency_graph_t generateHollowDiagnosticOutput(
186-
const DependencyScanDiagnosticCollector &ScanDiagnosticConsumer) {
186+
const DepScanInMemoryDiagnosticCollector &ScanDiagnosticConsumer) {
187187
// Create a dependency graph instance
188188
swiftscan_dependency_graph_t hollowResult = new swiftscan_dependency_graph_s;
189189

@@ -254,7 +254,7 @@ static swiftscan_dependency_graph_t generateHollowDiagnosticOutput(
254254
// imports but captures the diagnostics emitted during the attempted
255255
// scan query.
256256
static swiftscan_import_set_t generateHollowDiagnosticOutputImportSet(
257-
const DependencyScanDiagnosticCollector &ScanDiagnosticConsumer) {
257+
const DepScanInMemoryDiagnosticCollector &ScanDiagnosticConsumer) {
258258
// Create an dependency graph instance
259259
swiftscan_import_set_t hollowResult = new swiftscan_import_set_s;
260260
hollowResult->imports = c_string_utils::create_empty_set();
@@ -268,72 +268,57 @@ DependencyScanningTool::DependencyScanningTool()
268268
Alloc(), Saver(Alloc) {}
269269

270270
llvm::ErrorOr<swiftscan_dependency_graph_t>
271-
DependencyScanningTool::getDependencies(
272-
ArrayRef<const char *> Command,
273-
StringRef WorkingDirectory) {
274-
// There may be errors as early as in instance initialization, so we must ensure
275-
// we can catch those.
276-
auto ScanDiagnosticConsumer =
277-
std::make_shared<DependencyScanDiagnosticCollector>();
278-
271+
DependencyScanningTool::getDependencies(ArrayRef<const char *> Command,
272+
StringRef WorkingDirectory) {
279273
// The primary instance used to scan the query Swift source-code
280-
auto QueryContextOrErr = initCompilerInstanceForScan(Command,
281-
WorkingDirectory,
282-
ScanDiagnosticConsumer);
283-
if (QueryContextOrErr.getError())
284-
return generateHollowDiagnosticOutput(*ScanDiagnosticConsumer);
285-
286-
auto QueryContext = std::move(*QueryContextOrErr);
274+
auto QueryContext = createScanQueryContext(Command, WorkingDirectory);
275+
if (QueryContext.ScanInstance.getError())
276+
return generateHollowDiagnosticOutput(
277+
*QueryContext.InMemoryDiagnosticCollector);
278+
auto ScanInstance = QueryContext.ScanInstance->get();
287279

288280
// Local scan cache instance, wrapping the shared global cache.
289281
ModuleDependenciesCache cache(
290-
QueryContext.ScanInstance->getMainModule()->getNameStr().str(),
291-
QueryContext.ScanInstance->getInvocation().getModuleScanningHash());
282+
ScanInstance->getMainModule()->getNameStr().str(),
283+
ScanInstance->getInvocation().getModuleScanningHash());
292284
// Execute the scanning action, retrieving the in-memory result
293-
auto DependenciesOrErr = performModuleScan(*ScanningService,
294-
*QueryContext.ScanInstance.get(),
295-
cache,
296-
QueryContext.ScanDiagnostics.get());
285+
auto DependenciesOrErr =
286+
performModuleScan(*ScanningService, cache, QueryContext);
287+
297288
if (DependenciesOrErr.getError())
298-
return generateHollowDiagnosticOutput(*ScanDiagnosticConsumer);
289+
return generateHollowDiagnosticOutput(
290+
*QueryContext.InMemoryDiagnosticCollector);
299291

300292
return std::move(*DependenciesOrErr);
301293
}
302294

303295
llvm::ErrorOr<swiftscan_import_set_t>
304296
DependencyScanningTool::getImports(ArrayRef<const char *> Command,
305297
StringRef WorkingDirectory) {
306-
// There may be errors as early as in instance initialization, so we must ensure
307-
// we can catch those
308-
auto ScanDiagnosticConsumer = std::make_shared<DependencyScanDiagnosticCollector>();
309298
// The primary instance used to scan the query Swift source-code
310-
auto QueryContextOrErr = initCompilerInstanceForScan(Command,
311-
WorkingDirectory,
312-
ScanDiagnosticConsumer);
313-
if (QueryContextOrErr.getError())
314-
return generateHollowDiagnosticOutputImportSet(*ScanDiagnosticConsumer);
315-
316-
auto QueryContext = std::move(*QueryContextOrErr);
299+
auto QueryContext = createScanQueryContext(Command, WorkingDirectory);
300+
if (QueryContext.ScanInstance.getError())
301+
return generateHollowDiagnosticOutputImportSet(
302+
*QueryContext.InMemoryDiagnosticCollector);
303+
auto ScanInstance = QueryContext.ScanInstance->get();
317304

318305
// Local scan cache instance, wrapping the shared global cache.
319306
ModuleDependenciesCache cache(
320-
QueryContext.ScanInstance->getMainModule()->getNameStr().str(),
321-
QueryContext.ScanInstance->getInvocation().getModuleScanningHash());
322-
auto DependenciesOrErr = performModulePrescan(*ScanningService,
323-
*QueryContext.ScanInstance.get(),
324-
cache,
325-
QueryContext.ScanDiagnostics.get());
307+
ScanInstance->getMainModule()->getNameStr().str(),
308+
ScanInstance->getInvocation().getModuleScanningHash());
309+
// Execute the pre-scanning action, retrieving the in-memory result
310+
auto DependenciesOrErr =
311+
performModulePrescan(*ScanningService, cache, QueryContext);
312+
326313
if (DependenciesOrErr.getError())
327-
return generateHollowDiagnosticOutputImportSet(*ScanDiagnosticConsumer);
314+
return generateHollowDiagnosticOutputImportSet(
315+
*QueryContext.InMemoryDiagnosticCollector);
328316

329317
return std::move(*DependenciesOrErr);
330318
}
331319

332-
llvm::ErrorOr<ScanQueryInstance>
333-
DependencyScanningTool::initCompilerInstanceForScan(
334-
ArrayRef<const char *> CommandArgs,
335-
StringRef WorkingDir,
336-
std::shared_ptr<DependencyScanDiagnosticCollector> scannerDiagnosticsCollector) {
320+
ScanQueryContext DependencyScanningTool::createScanQueryContext(
321+
ArrayRef<const char *> CommandArgs, StringRef WorkingDir) {
337322
// The remainder of this method operates on shared state in the
338323
// scanning service
339324
llvm::sys::SmartScopedLock<true> Lock(DependencyScanningToolStateLock);
@@ -342,14 +327,20 @@ DependencyScanningTool::initCompilerInstanceForScan(
342327
// client-side API plumbing.
343328
llvm::sys::SmartScopedLock<true> TargetInfoLock(TargetInfoMutex);
344329

330+
// There may be errors as early as in instance initialization, so we must
331+
// ensure we can catch those
332+
auto ScannerDiagnosticsCollector =
333+
std::make_unique<DepScanInMemoryDiagnosticCollector>();
334+
345335
// State unique to an individual scan
346336
auto Instance = std::make_unique<CompilerInstance>();
347-
Instance->addDiagnosticConsumer(scannerDiagnosticsCollector.get());
337+
Instance->addDiagnosticConsumer(ScannerDiagnosticsCollector.get());
348338

349339
// Basic error checking on the arguments
350340
if (CommandArgs.empty()) {
351341
Instance->getDiags().diagnose(SourceLoc(), diag::error_no_frontend_args);
352-
return std::make_error_code(std::errc::invalid_argument);
342+
return ScanQueryContext{std::make_error_code(std::errc::invalid_argument),
343+
std::move(ScannerDiagnosticsCollector), nullptr};
353344
}
354345

355346
CompilerInvocation Invocation;
@@ -375,24 +366,39 @@ DependencyScanningTool::initCompilerInstanceForScan(
375366

376367
if (Invocation.parseArgs(Args, Instance->getDiags(),
377368
nullptr, WorkingDirectory, "/tmp/foo")) {
378-
return std::make_error_code(std::errc::invalid_argument);
369+
return ScanQueryContext{std::make_error_code(std::errc::invalid_argument),
370+
std::move(ScannerDiagnosticsCollector), nullptr};
379371
}
380372

381373
// Setup the instance
382374
std::string InstanceSetupError;
383-
if (Instance->setup(Invocation, InstanceSetupError)) {
384-
return std::make_error_code(std::errc::not_supported);
385-
}
375+
if (Instance->setup(Invocation, InstanceSetupError))
376+
return ScanQueryContext{std::make_error_code(std::errc::not_supported),
377+
std::move(ScannerDiagnosticsCollector), nullptr};
378+
386379
Invocation.getFrontendOptions().LLVMArgs.clear();
387380

388381
// Setup the caching service after the instance finishes setup.
389382
if (ScanningService->setupCachingDependencyScanningService(*Instance))
390-
return std::make_error_code(std::errc::invalid_argument);
383+
return ScanQueryContext{std::make_error_code(std::errc::invalid_argument),
384+
std::move(ScannerDiagnosticsCollector), nullptr};
391385

392386
(void)Instance->getMainModule();
393387

394-
return ScanQueryInstance{std::move(Instance),
395-
scannerDiagnosticsCollector};
388+
auto SerializedDiagnosticsOutputPath =
389+
Instance->getInvocation()
390+
.getSerializedDiagnosticsPathForAtMostOnePrimary();
391+
std::unique_ptr<DiagnosticConsumer> SerailizedDiagnosticsConsumer;
392+
if (!SerializedDiagnosticsOutputPath.empty()) {
393+
SerailizedDiagnosticsConsumer =
394+
swift::serialized_diagnostics::createThreadSafeConsumer(
395+
SerializedDiagnosticsOutputPath, false);
396+
Instance->addDiagnosticConsumer(SerailizedDiagnosticsConsumer.get());
397+
}
398+
399+
return ScanQueryContext{std::move(Instance),
400+
std::move(ScannerDiagnosticsCollector),
401+
std::move(SerailizedDiagnosticsConsumer)};
396402
}
397403

398404
} // namespace dependencies

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ ModuleDependencyScanningWorker::ModuleDependencyScanningWorker(
237237
ScanASTContext.SourceMgr, *workerDiagnosticEngine));
238238

239239
scanningASTDelegate = std::make_unique<InterfaceSubContextDelegateImpl>(
240-
workerASTContext->SourceMgr, &workerASTContext->Diags,
240+
workerASTContext->SourceMgr, workerDiagnosticEngine.get(),
241241
workerASTContext->SearchPathOpts, workerASTContext->LangOpts,
242242
workerASTContext->ClangImporterOpts, workerASTContext->CASOpts,
243243
workerCompilerInvocation->getFrontendOptions(),
@@ -314,14 +314,13 @@ ModuleDependencyScanningWorker::scanFilesystemForClangModuleDependency(
314314
clangScanningWorkingDirectoryPath, alreadySeenModules,
315315
lookupModuleOutput);
316316
if (!clangModuleDependencies) {
317-
auto errorStr = toString(clangModuleDependencies.takeError());
318-
// We ignore the "module 'foo' not found" error, the Swift dependency
319-
// scanner will report such an error only if all of the module loaders
320-
// fail as well.
321-
if (errorStr.find("fatal error: module '" + moduleName.str().str() +
322-
"' not found") == std::string::npos)
323-
workerASTContext->Diags.diagnose(
324-
SourceLoc(), diag::clang_dependency_scan_error, errorStr);
317+
llvm::handleAllErrors(clangModuleDependencies.takeError(), [this, &moduleName](
318+
const llvm::StringError &E) {
319+
auto &message = E.getMessage();
320+
if (message.find("fatal error: module '" + moduleName.str().str() +
321+
"' not found") == std::string::npos)
322+
workerDiagnosticEngine->diagnose(SourceLoc(), diag::clang_dependency_scan_error, message);
323+
});
325324
return std::nullopt;
326325
}
327326

@@ -355,7 +354,7 @@ ModuleDependencyScanningWorker::scanHeaderDependenciesOfSwiftModule(
355354
auto clangModuleDependencies = scanHeaderDependencies();
356355
if (!clangModuleDependencies) {
357356
auto errorStr = toString(clangModuleDependencies.takeError());
358-
workerASTContext->Diags.diagnose(
357+
workerDiagnosticEngine->diagnose(
359358
SourceLoc(), diag::clang_header_dependency_scan_error, errorStr);
360359
return std::nullopt;
361360
}

0 commit comments

Comments
 (0)