Skip to content

Commit 2934394

Browse files
authored
Merge pull request #84064 from artemcm/DepScanWorkersNoShareDiagEngine
[Dependency Scanning] Give each scanner worker a unique Diagnostic Engine
2 parents c75932b + 1cd1193 commit 2934394

File tree

5 files changed

+132
-38
lines changed

5 files changed

+132
-38
lines changed

include/swift/DependencyScan/DependencyScanningTool.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,33 @@ struct ScanQueryInstance {
3131
std::shared_ptr<DependencyScanDiagnosticCollector> ScanDiagnostics;
3232
};
3333

34+
/// Pure virtual Diagnostic consumer intended for collecting
35+
/// emitted diagnostics in a thread-safe fashion
36+
class ThreadSafeDiagnosticCollector : public DiagnosticConsumer {
37+
private:
38+
llvm::sys::SmartMutex<true> DiagnosticConsumerStateLock;
39+
void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) final;
40+
41+
protected:
42+
virtual void addDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) = 0;
43+
44+
public:
45+
ThreadSafeDiagnosticCollector() {}
46+
bool finishProcessing() final { return false; }
47+
};
48+
3449
/// Diagnostic consumer that simply collects the diagnostics emitted so-far
35-
class DependencyScanDiagnosticCollector : public DiagnosticConsumer {
50+
class DependencyScanDiagnosticCollector : public ThreadSafeDiagnosticCollector {
3651
private:
3752
struct ScannerDiagnosticInfo {
3853
std::string Message;
3954
llvm::SourceMgr::DiagKind Severity;
4055
std::optional<ScannerImportStatementInfo::ImportDiagnosticLocationInfo> ImportLocation;
4156
};
4257
std::vector<ScannerDiagnosticInfo> Diagnostics;
43-
llvm::sys::SmartMutex<true> ScanningDiagnosticConsumerStateLock;
44-
45-
void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override;
4658

4759
protected:
48-
virtual void addDiagnostic(SourceManager &SM, const DiagnosticInfo &Info);
60+
void addDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override;
4961

5062
public:
5163
friend DependencyScanningTool;

include/swift/DependencyScan/ModuleDependencyScanner.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ class ModuleDependencyScanningWorker {
107107

108108
// Worker-specific instance of CompilerInvocation
109109
std::unique_ptr<CompilerInvocation> workerCompilerInvocation;
110+
// Worker-specific diagnostic engine
111+
std::unique_ptr<DiagnosticEngine> workerDiagnosticEngine;
110112
// Worker-specific instance of ASTContext
111113
std::unique_ptr<ASTContext> workerASTContext;
112114
// An AST delegate for interface scanning.

lib/DependencyScan/DependencyScanningTool.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ llvm::ErrorOr<swiftscan_string_ref_t> getTargetInfo(ArrayRef<const char *> Comma
6464
return c_string_utils::create_clone(ResultStr.c_str());
6565
}
6666

67-
void DependencyScanDiagnosticCollector::handleDiagnostic(SourceManager &SM,
67+
void ThreadSafeDiagnosticCollector::handleDiagnostic(SourceManager &SM,
6868
const DiagnosticInfo &Info) {
69+
llvm::sys::SmartScopedLock<true> Lock(DiagnosticConsumerStateLock);
6970
addDiagnostic(SM, Info);
7071
for (auto ChildInfo : Info.ChildDiagnosticInfo) {
7172
addDiagnostic(SM, *ChildInfo);
@@ -74,8 +75,6 @@ void DependencyScanDiagnosticCollector::handleDiagnostic(SourceManager &SM,
7475

7576
void DependencyScanDiagnosticCollector::addDiagnostic(
7677
SourceManager &SM, const DiagnosticInfo &Info) {
77-
llvm::sys::SmartScopedLock<true> Lock(ScanningDiagnosticConsumerStateLock);
78-
7978
// Determine what kind of diagnostic we're emitting.
8079
llvm::SourceMgr::DiagKind SMKind;
8180
switch (Info.Kind) {

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -216,37 +216,45 @@ ModuleDependencyScanningWorker::ModuleDependencyScanningWorker(
216216
llvm::PrefixMapper *Mapper, DiagnosticEngine &Diagnostics)
217217
: workerCompilerInvocation(
218218
std::make_unique<CompilerInvocation>(ScanCompilerInvocation)),
219-
workerASTContext(std::unique_ptr<ASTContext>(
220-
ASTContext::get(workerCompilerInvocation->getLangOptions(),
221-
workerCompilerInvocation->getTypeCheckerOptions(),
222-
workerCompilerInvocation->getSILOptions(),
223-
workerCompilerInvocation->getSearchPathOptions(),
224-
workerCompilerInvocation->getClangImporterOptions(),
225-
workerCompilerInvocation->getSymbolGraphOptions(),
226-
workerCompilerInvocation->getCASOptions(),
227-
workerCompilerInvocation->getSerializationOptions(),
228-
ScanASTContext.SourceMgr, Diagnostics))),
229-
scanningASTDelegate(std::make_unique<InterfaceSubContextDelegateImpl>(
230-
workerASTContext->SourceMgr, &workerASTContext->Diags,
231-
workerASTContext->SearchPathOpts, workerASTContext->LangOpts,
232-
workerASTContext->ClangImporterOpts, workerASTContext->CASOpts,
233-
workerCompilerInvocation->getFrontendOptions(),
234-
/* buildModuleCacheDirIfAbsent */ false,
235-
getModuleCachePathFromClang(
236-
ScanASTContext.getClangModuleLoader()->getClangInstance()),
237-
workerCompilerInvocation->getFrontendOptions()
238-
.PrebuiltModuleCachePath,
239-
workerCompilerInvocation->getFrontendOptions()
240-
.BackupModuleInterfaceDir,
241-
workerCompilerInvocation->getFrontendOptions().CacheReplayPrefixMap,
242-
workerCompilerInvocation->getFrontendOptions()
243-
.SerializeModuleInterfaceDependencyHashes,
244-
workerCompilerInvocation->getFrontendOptions()
245-
.shouldTrackSystemDependencies(),
246-
RequireOSSAModules_t(SILOptions))),
247219
clangScanningTool(*globalScanningService.ClangScanningService,
248220
getClangScanningFS(CAS, ScanASTContext)),
249221
CAS(CAS), ActionCache(ActionCache) {
222+
// Instantiate a worker-specific diagnostic engine and copy over
223+
// the scanner's diagnostic consumers (expected to be thread-safe).
224+
workerDiagnosticEngine = std::make_unique<DiagnosticEngine>(ScanASTContext.SourceMgr);
225+
for (auto &scannerDiagConsumer : Diagnostics.getConsumers())
226+
workerDiagnosticEngine->addConsumer(*scannerDiagConsumer);
227+
228+
workerASTContext = std::unique_ptr<ASTContext>(
229+
ASTContext::get(workerCompilerInvocation->getLangOptions(),
230+
workerCompilerInvocation->getTypeCheckerOptions(),
231+
workerCompilerInvocation->getSILOptions(),
232+
workerCompilerInvocation->getSearchPathOptions(),
233+
workerCompilerInvocation->getClangImporterOptions(),
234+
workerCompilerInvocation->getSymbolGraphOptions(),
235+
workerCompilerInvocation->getCASOptions(),
236+
workerCompilerInvocation->getSerializationOptions(),
237+
ScanASTContext.SourceMgr, *workerDiagnosticEngine));
238+
239+
scanningASTDelegate = std::make_unique<InterfaceSubContextDelegateImpl>(
240+
workerASTContext->SourceMgr, &workerASTContext->Diags,
241+
workerASTContext->SearchPathOpts, workerASTContext->LangOpts,
242+
workerASTContext->ClangImporterOpts, workerASTContext->CASOpts,
243+
workerCompilerInvocation->getFrontendOptions(),
244+
/* buildModuleCacheDirIfAbsent */ false,
245+
getModuleCachePathFromClang(
246+
ScanASTContext.getClangModuleLoader()->getClangInstance()),
247+
workerCompilerInvocation->getFrontendOptions()
248+
.PrebuiltModuleCachePath,
249+
workerCompilerInvocation->getFrontendOptions()
250+
.BackupModuleInterfaceDir,
251+
workerCompilerInvocation->getFrontendOptions().CacheReplayPrefixMap,
252+
workerCompilerInvocation->getFrontendOptions()
253+
.SerializeModuleInterfaceDependencyHashes,
254+
workerCompilerInvocation->getFrontendOptions()
255+
.shouldTrackSystemDependencies(),
256+
RequireOSSAModules_t(SILOptions));
257+
250258
auto loader = std::make_unique<PluginLoader>(
251259
*workerASTContext, /*DepTracker=*/nullptr,
252260
workerCompilerInvocation->getFrontendOptions().CacheReplayPrefixMap,

unittests/DependencyScan/ModuleDeps.cpp

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,6 @@ public func funcB() { }\n"));
307307
for (auto &command : CommandStr)
308308
Command.push_back(command.c_str());
309309

310-
auto ScanDiagnosticConsumer = std::make_shared<DependencyScanDiagnosticCollector>();
311-
312310
auto DependenciesOrErr = ScannerTool.getDependencies(Command, {});
313311

314312
// Ensure a hollow output with diagnostic info is produced
@@ -329,3 +327,78 @@ public func funcB() { }\n"));
329327
ASSERT_TRUE(Dependencies->dependencies->modules[0]->link_libraries->count == 0);
330328
swiftscan_dependency_graph_dispose(Dependencies);
331329
}
330+
331+
TEST_F(ScanTest, TestStressConcurrentDiagnostics) {
332+
SmallString<256> tempDir;
333+
ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("ScanTest.TestStressConcurrentDiagnostics", tempDir));
334+
SWIFT_DEFER { llvm::sys::fs::remove_directories(tempDir); };
335+
336+
// Create includes
337+
std::string IncludeDirPath = createFilename(tempDir, "include");
338+
ASSERT_FALSE(llvm::sys::fs::create_directory(IncludeDirPath));
339+
std::string CHeadersDirPath = createFilename(IncludeDirPath, "CHeaders");
340+
ASSERT_FALSE(llvm::sys::fs::create_directory(CHeadersDirPath));
341+
342+
// Create test input file
343+
std::string TestPathStr = createFilename(tempDir, "foo.swift");
344+
345+
// Create imported module C modulemap/headers
346+
std::string modulemapContent = "";
347+
std::string testFileContent = "";
348+
for (int i = 0; i < 100; ++i) {
349+
std::string headerName = "A_" + std::to_string(i) + ".h";
350+
std::string headerContent = "void funcA_" + std::to_string(i) + "(void);";
351+
ASSERT_FALSE(
352+
emitFileWithContents(CHeadersDirPath, headerName, headerContent));
353+
354+
std::string moduleMapEntry = "module A_" + std::to_string(i) + "{\n";
355+
moduleMapEntry.append("header \"A_" + std::to_string(i) + ".h\"\n");
356+
moduleMapEntry.append("export *\n");
357+
moduleMapEntry.append("}\n");
358+
modulemapContent.append(moduleMapEntry);
359+
testFileContent.append("import A_" + std::to_string(i) + "\n");
360+
}
361+
362+
ASSERT_FALSE(emitFileWithContents(tempDir, "foo.swift", testFileContent));
363+
ASSERT_FALSE(
364+
emitFileWithContents(CHeadersDirPath, "module.modulemap", modulemapContent));
365+
366+
// Paths to shims and stdlib
367+
llvm::SmallString<128> ShimsLibDir = StdLibDir;
368+
llvm::sys::path::append(ShimsLibDir, "shims");
369+
auto Target = llvm::Triple(llvm::sys::getDefaultTargetTriple());
370+
llvm::sys::path::append(StdLibDir, getPlatformNameForTriple(Target));
371+
372+
std::vector<std::string> BaseCommandStrArr = {
373+
TestPathStr,
374+
std::string("-I ") + CHeadersDirPath,
375+
std::string("-I ") + StdLibDir.str().str(),
376+
std::string("-I ") + ShimsLibDir.str().str(),
377+
// Pass in a flag which will cause every instantiation of
378+
// the clang scanner to fail with "unknown argument"
379+
"-Xcc",
380+
"-foobar"
381+
};
382+
383+
std::vector<std::string> CommandStr = BaseCommandStrArr;
384+
CommandStr.push_back("-module-name");
385+
CommandStr.push_back("testConcurrentDiags");
386+
387+
// On Windows we need to add an extra escape for path separator characters because otherwise
388+
// the command line tokenizer will treat them as escape characters.
389+
for (size_t i = 0; i < CommandStr.size(); ++i) {
390+
std::replace(CommandStr[i].begin(), CommandStr[i].end(), '\\', '/');
391+
}
392+
std::vector<const char*> Command;
393+
for (auto &command : CommandStr)
394+
Command.push_back(command.c_str());
395+
396+
auto DependenciesOrErr = ScannerTool.getDependencies(Command, {});
397+
398+
// Ensure a hollow output with diagnostic info is produced
399+
ASSERT_FALSE(DependenciesOrErr.getError());
400+
auto Dependencies = DependenciesOrErr.get();
401+
auto Diagnostics = Dependencies->diagnostics;
402+
ASSERT_TRUE(Diagnostics->count > 100);
403+
swiftscan_dependency_graph_dispose(Dependencies);
404+
}

0 commit comments

Comments
 (0)