Skip to content

Commit 68a68cb

Browse files
Merge pull request swiftlang#27635 from varungandhi-apple/vg-robustify-module-trace-emission
Make module trace emission more robust.
2 parents 7efaad6 + 903add2 commit 68a68cb

File tree

13 files changed

+286
-86
lines changed

13 files changed

+286
-86
lines changed

include/swift/Frontend/ModuleInterfaceLoader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase {
151151
AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename,
152152
StringRef ModuleDocFilename,
153153
StringRef ModuleSourceInfoFilename,
154+
SmallVectorImpl<char> *ModuleInterfacePath,
154155
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
155156
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer,
156157
std::unique_ptr<llvm::MemoryBuffer> *ModuleSourceInfoBuffer) override;

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ enum class ModuleLoadingMode {
3131
OnlySerialized
3232
};
3333

34-
/// Common functionality shared between \c SerializedModuleLoader and
35-
/// \c ModuleInterfaceLoader.
34+
/// Common functionality shared between \c SerializedModuleLoader,
35+
/// \c ModuleInterfaceLoader and \c MemoryBufferSerializedModuleLoader.
3636
class SerializedModuleLoaderBase : public ModuleLoader {
3737
/// A { module, generation # } pair.
3838
using LoadedModulePair = std::pair<std::unique_ptr<ModuleFile>, unsigned>;
@@ -53,6 +53,7 @@ class SerializedModuleLoaderBase : public ModuleLoader {
5353

5454
using AccessPathElem = std::pair<Identifier, SourceLoc>;
5555
bool findModule(AccessPathElem moduleID,
56+
SmallVectorImpl<char> *moduleInterfacePath,
5657
std::unique_ptr<llvm::MemoryBuffer> *moduleBuffer,
5758
std::unique_ptr<llvm::MemoryBuffer> *moduleDocBuffer,
5859
std::unique_ptr<llvm::MemoryBuffer> *moduleSourceInfoBuffer,
@@ -74,6 +75,7 @@ class SerializedModuleLoaderBase : public ModuleLoader {
7475
AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename,
7576
StringRef ModuleDocFilename,
7677
StringRef ModuleSourceInfoFilename,
78+
SmallVectorImpl<char> *ModuleInterfacePath,
7779
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
7880
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer,
7981
std::unique_ptr<llvm::MemoryBuffer> *ModuleSourceInfoBuffer) = 0;
@@ -127,6 +129,7 @@ class SerializedModuleLoaderBase : public ModuleLoader {
127129
/// If the AST cannot be loaded and \p diagLoc is present, a diagnostic is
128130
/// printed. (Note that \p diagLoc is allowed to be invalid.)
129131
FileUnit *loadAST(ModuleDecl &M, Optional<SourceLoc> diagLoc,
132+
StringRef moduleInterfacePath,
130133
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer,
131134
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer,
132135
std::unique_ptr<llvm::MemoryBuffer> moduleSourceInfoInputBuffer,
@@ -178,6 +181,7 @@ class SerializedModuleLoader : public SerializedModuleLoaderBase {
178181
AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename,
179182
StringRef ModuleDocFilename,
180183
StringRef ModuleSourceInfoFilename,
184+
SmallVectorImpl<char> *ModuleInterfacePath,
181185
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
182186
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer,
183187
std::unique_ptr<llvm::MemoryBuffer> *ModuleSourceInfoBuffer) override;
@@ -223,6 +227,7 @@ class MemoryBufferSerializedModuleLoader : public SerializedModuleLoaderBase {
223227
AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename,
224228
StringRef ModuleDocFilename,
225229
StringRef ModuleSourceInfoFilename,
230+
SmallVectorImpl<char> *ModuleInterfacePath,
226231
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
227232
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer,
228233
std::unique_ptr<llvm::MemoryBuffer> *ModuleSourceInfoBuffer) override;

lib/AST/ModuleLoader.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ DependencyTracker::addDependency(StringRef File, bool IsSystem) {
3939
// DependencyTracker exposes an interface that (intentionally) does not talk
4040
// about clang at all, nor about missing deps. It does expose an IsSystem
4141
// dimension, which we accept and pass along to the clang DependencyCollector.
42-
// along to the clang DependencyCollector.
4342
clangCollector->maybeAddDependency(File, /*FromModule=*/false,
4443
IsSystem, /*IsModuleFile=*/false,
4544
/*IsMissing=*/false);

lib/Frontend/Frontend.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -931,8 +931,8 @@ bool CompilerInstance::parsePartialModulesAndLibraryFiles(
931931
// Parse all the partial modules first.
932932
for (auto &PM : PartialModules) {
933933
assert(PM.ModuleBuffer);
934-
if (!SML->loadAST(*MainModule, SourceLoc(), std::move(PM.ModuleBuffer),
935-
std::move(PM.ModuleDocBuffer),
934+
if (!SML->loadAST(*MainModule, SourceLoc(), /*moduleInterfacePath*/"",
935+
std::move(PM.ModuleBuffer), std::move(PM.ModuleDocBuffer),
936936
std::move(PM.ModuleSourceInfoBuffer), /*isFramework*/false,
937937
/*treatAsPartialModule*/true))
938938
hadLoadError = true;

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,7 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory(
990990
AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename,
991991
StringRef ModuleDocFilename,
992992
StringRef ModuleSourceInfoFilename,
993+
SmallVectorImpl<char> *ModuleInterfacePath,
993994
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
994995
std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer,
995996
std::unique_ptr<llvm::MemoryBuffer> *ModuleSourceInfoBuffer) {
@@ -1036,6 +1037,8 @@ std::error_code ModuleInterfaceLoader::findModuleFilesInDirectory(
10361037

10371038
if (ModuleBuffer) {
10381039
*ModuleBuffer = std::move(*ModuleBufferOrErr);
1040+
if (ModuleInterfacePath)
1041+
*ModuleInterfacePath = InPath;
10391042
}
10401043
// Open .swiftsourceinfo file if it's present.
10411044
SerializedModuleLoaderBase::openModuleSourceInfoFileIfPresent(ModuleID,

lib/FrontendTool/FrontendTool.cpp

Lines changed: 166 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,165 @@ template <> struct ObjectTraits<LoadedModuleTraceFormat> {
237237
}
238238
}
239239

240+
/// Compute the per-module information to be recorded in the trace file.
241+
//
242+
// The most interesting/tricky thing here is _which_ paths get recorded in
243+
// the trace file as dependencies. It depends on how the module was synthesized.
244+
// The key points are:
245+
//
246+
// 1. Paths to swiftmodules in the module cache or in the prebuilt cache are not
247+
// recorded - Precondition: the corresponding path to the swiftinterface must
248+
// already be present as a key in pathToModuleDecl.
249+
// 2. swiftmodules next to a swiftinterface are saved if they are up-to-date.
250+
//
251+
// FIXME: Use the VFS instead of handling paths directly. We are particularly
252+
// sloppy about handling relative paths in the dependency tracker.
253+
static void computeSwiftModuleTraceInfo(
254+
const SmallPtrSetImpl<ModuleDecl *> &importedModules,
255+
const llvm::DenseMap<StringRef, ModuleDecl *> &pathToModuleDecl,
256+
const DependencyTracker &depTracker,
257+
StringRef prebuiltCachePath,
258+
std::vector<SwiftModuleTraceInfo> &traceInfo) {
259+
260+
SmallString<256> buffer;
261+
262+
std::string errMsg;
263+
llvm::raw_string_ostream err(errMsg);
264+
265+
// FIXME: Use PrettyStackTrace instead.
266+
auto errorUnexpectedPath =
267+
[&pathToModuleDecl](llvm::raw_string_ostream &errStream) {
268+
errStream << "The module <-> path mapping we have is:\n";
269+
for (auto &m: pathToModuleDecl)
270+
errStream << m.second->getName() << " <-> " << m.first << '\n';
271+
llvm::report_fatal_error(errStream.str());
272+
};
273+
274+
using namespace llvm::sys;
275+
276+
auto computeAdjacentInterfacePath = [](SmallVectorImpl<char> &modPath) {
277+
auto swiftInterfaceExt =
278+
file_types::getExtension(file_types::TY_SwiftModuleInterfaceFile);
279+
path::replace_extension(modPath, swiftInterfaceExt);
280+
};
281+
282+
for (auto &depPath : depTracker.getDependencies()) {
283+
284+
// Decide if this is a swiftmodule based on the extension of the raw
285+
// dependency path, as the true file may have a different one.
286+
// For example, this might happen when the canonicalized path points to
287+
// a Content Addressed Storage (CAS) location.
288+
auto moduleFileType =
289+
file_types::lookupTypeForExtension(path::extension(depPath));
290+
auto isSwiftmodule =
291+
moduleFileType == file_types::TY_SwiftModuleFile;
292+
auto isSwiftinterface =
293+
moduleFileType == file_types::TY_SwiftModuleInterfaceFile;
294+
295+
if (!(isSwiftmodule || isSwiftinterface))
296+
continue;
297+
298+
auto dep = pathToModuleDecl.find(depPath);
299+
if (dep != pathToModuleDecl.end()) {
300+
// Great, we recognize the path! Check if the file is still around.
301+
302+
ModuleDecl *depMod = dep->second;
303+
if(depMod->isResilient() && !isSwiftinterface) {
304+
SmallString<256> moduleAdjacentInterfacePath(depPath);
305+
computeAdjacentInterfacePath(moduleAdjacentInterfacePath);
306+
// FIXME: The behavior of fs::exists for relative paths is undocumented.
307+
// Use something else instead?
308+
if (!fs::exists(moduleAdjacentInterfacePath)) {
309+
err << "The module " << depMod->getName().str() << " has library"
310+
<< " evolution enabled but we're recording a non-adjacent"
311+
<< " swiftmodule at\n" << depPath << "\nin the trace.";
312+
llvm::report_fatal_error(err.str());
313+
}
314+
buffer.clear();
315+
}
316+
317+
// FIXME: Better error handling
318+
StringRef realDepPath
319+
= fs::real_path(depPath, buffer, /*expand_tile*/true)
320+
? StringRef(depPath) // Couldn't find the canonical path, assume
321+
// this is good enough.
322+
: buffer.str();
323+
324+
traceInfo.push_back({
325+
/*Name=*/
326+
depMod->getName(),
327+
/*Path=*/
328+
realDepPath,
329+
// TODO: There is an edge case which is not handled here.
330+
// When we build a framework using -import-underlying-module, or an
331+
// app/test using -import-objc-header, we should look at the direct
332+
// imports of the bridging modules, and mark those as our direct
333+
// imports.
334+
/*IsImportedDirectly=*/
335+
importedModules.find(depMod) != importedModules.end(),
336+
/*SupportsLibraryEvolution=*/
337+
depMod->isResilient()
338+
});
339+
buffer.clear();
340+
341+
continue;
342+
}
343+
344+
// If the depTracker had an interface, that means that we must've
345+
// built a swiftmodule from that interface, so we should have that
346+
// filename available.
347+
if (isSwiftinterface) {
348+
err << "Unexpected path for swiftinterface file:\n" << depPath << "\n";
349+
errorUnexpectedPath(err);
350+
}
351+
352+
// Skip cached modules in the prebuilt cache. We will add the corresponding
353+
// swiftinterface from the SDK directly, but this isn't checked. :-/
354+
//
355+
// FIXME: This is incorrect if both paths are not relative w.r.t. to the
356+
// same root.
357+
if (StringRef(depPath).startswith(prebuiltCachePath))
358+
continue;
359+
360+
// If we have a swiftmodule next to an interface, that interface path will
361+
// be saved (not checked), so don't save the path to this swiftmodule.
362+
SmallString<256> moduleAdjacentInterfacePath(depPath);
363+
computeAdjacentInterfacePath(moduleAdjacentInterfacePath);
364+
if (pathToModuleDecl.find(moduleAdjacentInterfacePath)
365+
!= pathToModuleDecl.end())
366+
continue;
367+
368+
// FIXME: The behavior of fs::exists for relative paths is undocumented.
369+
// Use something else instead?
370+
if (fs::exists(moduleAdjacentInterfacePath)) {
371+
err << "Found swiftinterface at\n" << moduleAdjacentInterfacePath
372+
<< "\nbut it was not recorded\n";
373+
errorUnexpectedPath(err);
374+
}
375+
buffer.clear();
376+
377+
err << "Don't know how to handle the dependency at:\n" << depPath
378+
<< "\nfor module trace emission.\n";
379+
errorUnexpectedPath(err);
380+
}
381+
382+
// Almost a re-implementation of reversePathSortedFilenames :(.
383+
std::sort(
384+
traceInfo.begin(), traceInfo.end(),
385+
[](const SwiftModuleTraceInfo &m1, const SwiftModuleTraceInfo &m2) -> bool {
386+
return std::lexicographical_compare(
387+
m1.Path.rbegin(), m1.Path.rend(),
388+
m2.Path.rbegin(), m2.Path.rend());
389+
});
390+
}
391+
240392
static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
241393
DependencyTracker *depTracker,
394+
StringRef prebuiltCachePath,
242395
StringRef loadedModuleTracePath) {
243396
ASTContext &ctxt = mainModule->getASTContext();
244397
assert(!ctxt.hadError()
245-
&& "We may not be able to emit a proper trace if there was an error.");
398+
&& "We should've already exited earlier if there was an error.");
246399

247400
if (loadedModuleTracePath.empty())
248401
return false;
@@ -269,67 +422,22 @@ static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
269422
llvm::DenseMap<StringRef, ModuleDecl *> pathToModuleDecl;
270423
for (auto &module : ctxt.LoadedModules) {
271424
ModuleDecl *loadedDecl = module.second;
272-
assert(loadedDecl && "Expected loaded module to be non-null.");
425+
if (!loadedDecl)
426+
llvm::report_fatal_error("Expected loaded modules to be non-null.");
273427
if (loadedDecl == mainModule)
274428
continue;
275-
assert(!loadedDecl->getModuleFilename().empty()
276-
&& ("Don't know how to handle modules with empty names."
277-
" One potential reason for getting an empty module name might"
278-
" be that the module could not be deserialized correctly."));
429+
if (loadedDecl->getModuleFilename().empty())
430+
llvm::report_fatal_error(
431+
"Don't know how to handle modules with empty names."
432+
" One potential reason for getting an empty module name might"
433+
" be that the module could not be deserialized correctly.");
279434
pathToModuleDecl.insert(
280435
std::make_pair(loadedDecl->getModuleFilename(), loadedDecl));
281436
}
282437

283438
std::vector<SwiftModuleTraceInfo> swiftModules;
284-
SmallString<256> buffer;
285-
for (auto &depPath : depTracker->getDependencies()) {
286-
StringRef realDepPath;
287-
// FIXME: appropriate error handling
288-
if (llvm::sys::fs::real_path(depPath, buffer,/*expand_tilde=*/true))
289-
// Couldn't find the canonical path, so let's just assume the old one was
290-
// canonical (enough).
291-
realDepPath = depPath;
292-
else
293-
realDepPath = buffer.str();
294-
295-
// Decide if this is a swiftmodule based on the extension of the raw
296-
// dependency path, as the true file may have a different one.
297-
// For example, this might happen when the canonicalized path points to
298-
// a Content Addressed Storage (CAS) location.
299-
auto moduleFileType =
300-
file_types::lookupTypeForExtension(llvm::sys::path::extension(depPath));
301-
if (moduleFileType == file_types::TY_SwiftModuleFile
302-
|| moduleFileType == file_types::TY_SwiftModuleInterfaceFile) {
303-
auto dep = pathToModuleDecl.find(depPath);
304-
assert(dep != pathToModuleDecl.end()
305-
&& "Dependency must've been loaded.");
306-
ModuleDecl *depMod = dep->second;
307-
swiftModules.push_back({
308-
/*Name=*/
309-
depMod->getName(),
310-
/*Path=*/
311-
realDepPath,
312-
// TODO: There is an edge case which is not handled here.
313-
// When we build a framework using -import-underlying-module, or an
314-
// app/test using -import-objc-header, we should look at the direct
315-
// imports of the bridging modules, and mark those as our direct
316-
// imports.
317-
/*IsImportedDirectly=*/
318-
importedModules.find(depMod) != importedModules.end(),
319-
/*SupportsLibraryEvolution=*/
320-
depMod->isResilient()
321-
});
322-
}
323-
}
324-
325-
// Almost a re-implementation of reversePathSortedFilenames :(.
326-
std::sort(
327-
swiftModules.begin(), swiftModules.end(),
328-
[](const SwiftModuleTraceInfo &m1, const SwiftModuleTraceInfo &m2) -> bool {
329-
return std::lexicographical_compare(
330-
m1.Path.rbegin(), m1.Path.rend(),
331-
m2.Path.rbegin(), m2.Path.rend());
332-
});
439+
computeSwiftModuleTraceInfo(importedModules, pathToModuleDecl, *depTracker,
440+
prebuiltCachePath, swiftModules);
333441

334442
LoadedModuleTraceFormat trace = {
335443
/*version=*/LoadedModuleTraceFormat::CurrentVersion,
@@ -339,8 +447,7 @@ static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
339447
};
340448

341449
// raw_fd_ostream is unbuffered, and we may have multiple processes writing,
342-
// so first write the whole thing into memory and dump out that buffer to the
343-
// file.
450+
// so first write to memory and then dump the buffer to the trace file.
344451
std::string stringBuffer;
345452
{
346453
llvm::raw_string_ostream memoryBuffer(stringBuffer);
@@ -349,7 +456,6 @@ static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
349456
json::jsonize(jsonOutput, trace, /*Required=*/true);
350457
}
351458
stringBuffer += "\n";
352-
353459
out << stringBuffer;
354460

355461
return true;
@@ -362,7 +468,8 @@ emitLoadedModuleTraceForAllPrimariesIfNeeded(ModuleDecl *mainModule,
362468
return opts.InputsAndOutputs.forEachInputProducingSupplementaryOutput(
363469
[&](const InputFile &input) -> bool {
364470
return emitLoadedModuleTraceIfNeeded(
365-
mainModule, depTracker, input.loadedModuleTracePath());
471+
mainModule, depTracker, opts.PrebuiltModuleCachePath,
472+
input.loadedModuleTracePath());
366473
});
367474
}
368475

lib/Serialization/ModuleFile.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,8 @@ class ModuleFile
639639
/// \returns Whether the module was successfully loaded, or what went wrong
640640
/// if it was not.
641641
static serialization::ValidationInfo
642-
load(std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer,
642+
load(StringRef moduleInterfacePath,
643+
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer,
643644
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer,
644645
std::unique_ptr<llvm::MemoryBuffer> moduleSourceInfoInputBuffer,
645646
bool isFramework, std::unique_ptr<ModuleFile> &theModule,
@@ -649,6 +650,8 @@ class ModuleFile
649650
std::move(moduleDocInputBuffer),
650651
std::move(moduleSourceInfoInputBuffer),
651652
isFramework, info, extInfo));
653+
if (!moduleInterfacePath.empty())
654+
theModule->ModuleInterfacePath = moduleInterfacePath;
652655
return info;
653656
}
654657

0 commit comments

Comments
 (0)