blob: 8f51a0ebe1ad74365a09a979542b9cd30515c1d5 [file] [log] [blame]
//===--- ModuleTrace.cpp -- Emit a trace of all loaded Swift modules ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "Dependencies.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/Module.h"
#include "swift/Basic/FileTypes.h"
#include "swift/Basic/JSONSerialization.h"
#include "swift/Frontend/FrontendOptions.h"
#include "clang/Basic/Module.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/YAMLTraits.h"
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#else
#include <io.h>
#endif
using namespace swift;
namespace {
struct SwiftModuleTraceInfo {
Identifier Name;
std::string Path;
bool IsImportedDirectly;
bool SupportsLibraryEvolution;
};
struct LoadedModuleTraceFormat {
static const unsigned CurrentVersion = 2;
unsigned Version;
Identifier Name;
std::string Arch;
std::vector<SwiftModuleTraceInfo> SwiftModules;
};
} // namespace
namespace swift {
namespace json {
template <> struct ObjectTraits<SwiftModuleTraceInfo> {
static void mapping(Output &out, SwiftModuleTraceInfo &contents) {
StringRef name = contents.Name.str();
out.mapRequired("name", name);
out.mapRequired("path", contents.Path);
out.mapRequired("isImportedDirectly", contents.IsImportedDirectly);
out.mapRequired("supportsLibraryEvolution",
contents.SupportsLibraryEvolution);
}
};
// Version notes:
// 1. Keys: name, arch, swiftmodules
// 2. New keys: version, swiftmodulesDetailedInfo
template <> struct ObjectTraits<LoadedModuleTraceFormat> {
static void mapping(Output &out, LoadedModuleTraceFormat &contents) {
out.mapRequired("version", contents.Version);
StringRef name = contents.Name.str();
out.mapRequired("name", name);
out.mapRequired("arch", contents.Arch);
// The 'swiftmodules' key is kept for backwards compatibility.
std::vector<std::string> moduleNames;
for (auto &m : contents.SwiftModules)
moduleNames.push_back(m.Path);
out.mapRequired("swiftmodules", moduleNames);
out.mapRequired("swiftmodulesDetailedInfo", contents.SwiftModules);
}
};
} // namespace json
} // namespace swift
static bool isClangOverlayOf(ModuleDecl *potentialOverlay,
ModuleDecl *potentialUnderlying) {
return !potentialOverlay->isNonSwiftModule() &&
potentialUnderlying->isNonSwiftModule() &&
potentialOverlay->getName() == potentialUnderlying->getName();
}
// TODO: Delete this once changes from https://reviews.llvm.org/D83449 land on
// apple/llvm-project's swift/main branch.
template <typename SetLike, typename Item>
static bool contains(const SetLike &setLike, Item item) {
return setLike.find(item) != setLike.end();
}
/// Get a set of modules imported by \p module.
///
/// By default, all imports are included.
static void getImmediateImports(
ModuleDecl *module, SmallPtrSetImpl<ModuleDecl *> &imports,
ModuleDecl::ImportFilter importFilter = {
ModuleDecl::ImportFilterKind::Exported,
ModuleDecl::ImportFilterKind::Default,
ModuleDecl::ImportFilterKind::ImplementationOnly,
ModuleDecl::ImportFilterKind::SPIAccessControl,
ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay}) {
SmallVector<ImportedModule, 8> importList;
module->getImportedModules(importList, importFilter);
for (ImportedModule &import : importList)
imports.insert(import.importedModule);
}
namespace {
/// Helper type for computing (approximate) information about ABI-dependencies.
///
/// This misses out on details such as typealiases and more.
/// See the "isImportedDirectly" field above for more details.
class ABIDependencyEvaluator {
/// Map of ABIs exported by a particular module, excluding itself.
///
/// For example, consider (primed letters represent Clang modules):
/// \code
/// - A is @_exported-imported by B
/// - B is #imported by C' (via a compiler-generated umbrella header)
/// - C' is @_exported-imported by C (Swift overlay)
/// - D' is #imported by E'
/// - D' is @_exported-imported by D (Swift overlay)
/// - E' is @_exported-imported by E (Swift overlay)
/// \endcode
///
/// Then the \c abiExportMap will be
/// \code
/// { A: {}, B: {A}, C: {B}, C': {B}, D: {}, D': {}, E: {D}, E': {D'} }
/// \endcode
///
/// \b WARNING: Use \c reexposeImportedABI instead of inserting directly.
llvm::DenseMap<ModuleDecl *, llvm::DenseSet<ModuleDecl *>> abiExportMap;
/// Stack for depth-first traversal.
SmallVector<ModuleDecl *, 32> searchStack;
llvm::DenseSet<ModuleDecl *> visited;
/// Helper function to handle invariant violations as crashes in debug mode.
void crashOnInvariantViolation(
llvm::function_ref<void(llvm::raw_string_ostream &)> f) const;
/// Computes the ABI exports for \p importedModule and adds them to
/// \p module's ABI exports.
///
/// If \p includeImportedModule is true, also adds \p importedModule to
/// \p module's ABI exports.
///
/// Correct way to add entries to \c abiExportMap.
void reexposeImportedABI(ModuleDecl *module, ModuleDecl *importedModule,
bool includeImportedModule = true);
/// Check if a Swift module is an overlay for some Clang module.
///
/// FIXME: Delete this hack once SR-13363 is fixed and ModuleDecl has the
/// right API which we can use directly.
bool isOverlayOfClangModule(ModuleDecl *swiftModule);
/// Check for cases where we have a fake cycle through an overlay.
///
/// Sometimes, we have fake cycles in the import graph due to the Clang
/// importer injecting overlays between Clang modules. These don't represent
/// an actual cycle in the build, so we should ignore them.
///
/// We check this lazily after detecting a cycle because it is difficult to
/// determine at the point where we see the overlay whether it was incorrectly
/// injected by the Clang importer or whether any of its imports will
/// eventually lead to a cycle.
///
/// For more details, see [NOTE: ABIDependencyEvaluator-fake-cycle-detection]
///
/// \param startOfCycle A pointer to the element of \c searchStack where
/// the module \em first appeared.
///
/// \pre The module on top of \c searchStack is the same module as
/// *startOfCycle.
///
/// \pre searchStack.begin() <= startOfCycle < searchStack.end()
bool isFakeCycleThroughOverlay(ModuleDecl **startOfCycle);
/// Recursive step in computing ABI dependencies.
///
/// Use this method instead of using the \c forClangModule/\c forSwiftModule
/// methods.
void computeABIDependenciesForModule(ModuleDecl *module);
void computeABIDependenciesForSwiftModule(ModuleDecl *module);
void computeABIDependenciesForClangModule(ModuleDecl *module);
static void printModule(const ModuleDecl *module, llvm::raw_ostream &os);
template <typename SetLike>
static void printModuleSet(const SetLike &set, llvm::raw_ostream &os);
public:
ABIDependencyEvaluator() = default;
ABIDependencyEvaluator(const ABIDependencyEvaluator &) = delete;
ABIDependencyEvaluator(ABIDependencyEvaluator &&) = default;
void getABIDependenciesForSwiftModule(
ModuleDecl *module, SmallPtrSetImpl<ModuleDecl *> &abiDependencies);
void printABIExportMap(llvm::raw_ostream &os) const;
};
} // end anonymous namespace
// See [NOTE: Bailing-vs-crashing-in-trace-emission].
// TODO: Use PrettyStackTrace instead?
void ABIDependencyEvaluator::crashOnInvariantViolation(
llvm::function_ref<void(llvm::raw_string_ostream &)> f) const {
#ifndef NDEBUG
std::string msg;
llvm::raw_string_ostream os(msg);
os << "error: invariant violation: ";
f(os);
llvm::report_fatal_error(os.str());
#endif
}
// [NOTE: Trace-Clang-submodule-complexity]
//
// A Clang module may have zero or more submodules. In practice, when traversing
// the imports of a module, we observe that different submodules of the same
// top-level module (almost) freely import each other. Despite this, we still
// need to conceptually traverse the tree formed by the submodule relationship
// (with the top-level module being the root).
//
// This needs to be taken care of in two ways:
// 1. We need to make sure we only go towards the leaves. It's okay if we "jump"
// branches, so long as we don't try to visit an ancestor when one of its
// descendants is still on the traversal stack, so that we don't end up with
// arbitrarily complex intra-module cycles.
// See also: [NOTE: Intra-module-leafwards-traversal].
// 2. When adding entries to the ABI export map, we need to avoid marking
// dependencies within the same top-level module. This step is needed in
// addition to step 1 to avoid creating cycles like
// Overlay -> Underlying -> Submodule -> Overlay.
void ABIDependencyEvaluator::reexposeImportedABI(ModuleDecl *module,
ModuleDecl *importedModule,
bool includeImportedModule) {
if (module == importedModule) {
crashOnInvariantViolation([&](llvm::raw_string_ostream &os) {
os << "module ";
printModule(module, os);
os << " imports itself!\n";
});
return;
}
auto addToABIExportMap = [this](ModuleDecl *module, ModuleDecl *reexport) {
if (module == reexport) {
crashOnInvariantViolation([&](llvm::raw_string_ostream &os) {
os << "expected module ";
printModule(reexport, os);
os << " to not re-export itself\n";
});
return;
}
if (reexport->isNonSwiftModule() && module->isNonSwiftModule() &&
module->getTopLevelModule() == reexport->getTopLevelModule()) {
// Dependencies within the same top-level Clang module are not useful.
// See also: [NOTE: Trace-Clang-submodule-complexity].
return;
}
// We only care about dependencies across top-level modules and we want to
// avoid exploding abiExportMap with submodules. So we only insert entries
// after calling getTopLevelModule().
if (::isClangOverlayOf(module, reexport)) {
// For overlays, we need to have a dependency on the underlying module.
// Otherwise, we might accidentally create a Swift -> Swift cycle.
abiExportMap[module].insert(
reexport->getTopLevelModule(/*preferOverlay*/ false));
return;
}
abiExportMap[module].insert(
reexport->getTopLevelModule(/*preferOverlay*/ true));
};
computeABIDependenciesForModule(importedModule);
if (includeImportedModule) {
addToABIExportMap(module, importedModule);
}
// Force creation of default value if missing. This prevents abiExportMap from
// growing (and moving) when calling addToABIExportMap. If abiExportMap gets
// moved, then abiExportMap[importedModule] will be moved, forcing us to
// create a defensive copy to avoid iterator invalidation on move.
(void)abiExportMap[module];
for (auto reexportedModule : abiExportMap[importedModule])
addToABIExportMap(module, reexportedModule);
}
bool ABIDependencyEvaluator::isOverlayOfClangModule(ModuleDecl *swiftModule) {
assert(!swiftModule->isNonSwiftModule());
llvm::SmallPtrSet<ModuleDecl *, 8> importList;
::getImmediateImports(swiftModule, importList,
{ModuleDecl::ImportFilterKind::Exported});
bool isOverlay =
llvm::any_of(importList, [&](ModuleDecl *importedModule) -> bool {
return isClangOverlayOf(swiftModule, importedModule);
});
return isOverlay;
}
// [NOTE: ABIDependencyEvaluator-fake-cycle-detection]
//
// First, let's consider a concrete example.
// - In Clang-land, ToyKit #imports CoreDoll.
// - The Swift overlay for CoreDoll imports both CoreDoll and ToyKit.
// Importing ToyKit from CoreDoll's overlay informally violates the layering
// of frameworks, but it doesn't actually create any cycles in the build
// dependencies.
// ┌───────────────────────────┐
// ┌───│ CoreDoll.swiftmodule │
// │ └───────────────────────────┘
// │ │
// import ToyKit @_exported import CoreDoll
// │ │
// │ │
// ▼ │
// ┌──────────────────────────┐ │
// │ ToyKit (ToyKit/ToyKit.h) │ │
// └──────────────────────────┘ │
// │ │
// #import <CoreDoll/CoreDoll.h> │
// │ │
// ▼ │
// ┌──────────────────────────────┐ │
// │CoreDoll (CoreDoll/CoreDoll.h)│◀──┘
// └──────────────────────────────┘
//
// Say we are trying to build a Swift module that imports ToyKit. Due to how
// module loading works, the Clang importer inserts the CoreDoll overlay
// between the ToyKit and CoreDoll Clang modules, creating a cycle in the
// import graph.
//
// ┌──────────────────────────┐
// │ ToyKit (ToyKit/ToyKit.h) │◀──────────┐
// └──────────────────────────┘ │
// │ │
// #import <CoreDoll/CoreDoll.h> import ToyKit
// │ │
// ▼ │
// ┌────────────────────────────┐ │
// │ CoreDoll.swiftmodule │─────────┘
// └────────────────────────────┘
// │
// @_exported import CoreDoll
// │
// ▼
// ┌──────────────────────────────┐
// │CoreDoll (CoreDoll/CoreDoll.h)│
// └──────────────────────────────┘
//
// This means that, at some point, searchStack will look like:
//
// [others] → ToyKit → CoreDoll (overlay) → ToyKit
//
// In the general case, there may be arbitrarily many modules in the cycle,
// including submodules.
//
// [others] → ToyKit → [others] → CoreDoll (overlay) → [others] → ToyKit
//
// where "[others]" indicates 0 or more modules of any kind.
//
// To detect this, we check that the start of the cycle is a Clang module and
// that there is at least one overlay between it and its recurrence at the end
// of the searchStack. If so, we assume we have detected a benign cycle which
// can be safely ignored.
bool ABIDependencyEvaluator::isFakeCycleThroughOverlay(
ModuleDecl **startOfCycle) {
assert(startOfCycle >= searchStack.begin() &&
startOfCycle < searchStack.end() &&
"startOfCycleIter points to an element in searchStack");
// The startOfCycle module must be a Clang module.
if (!(*startOfCycle)->isNonSwiftModule())
return false;
// Next, we must have zero or more modules followed by a Swift overlay for a
// Clang module.
return std::any_of(
startOfCycle + 1, searchStack.end(), [this](ModuleDecl *module) {
return !module->isNonSwiftModule() && isOverlayOfClangModule(module);
});
}
void ABIDependencyEvaluator::computeABIDependenciesForModule(
ModuleDecl *module) {
auto moduleIter = llvm::find(searchStack, module);
if (moduleIter != searchStack.end()) {
if (isFakeCycleThroughOverlay(moduleIter))
return;
crashOnInvariantViolation([&](llvm::raw_string_ostream &os) {
os << "unexpected cycle in import graph!\n";
for (auto m : searchStack) {
printModule(m, os);
if (!m->isNonSwiftModule()) {
os << " (isOverlay = " << isOverlayOfClangModule(m) << ")";
}
os << "\ndepends on ";
}
printModule(module, os);
os << '\n';
});
return;
}
if (::contains(visited, module))
return;
searchStack.push_back(module);
if (module->isNonSwiftModule())
computeABIDependenciesForClangModule(module);
else
computeABIDependenciesForSwiftModule(module);
searchStack.pop_back();
visited.insert(module);
}
void ABIDependencyEvaluator::computeABIDependenciesForSwiftModule(
ModuleDecl *module) {
SmallPtrSet<ModuleDecl *, 32> allImports;
::getImmediateImports(module, allImports);
for (auto import : allImports) {
computeABIDependenciesForModule(import);
if (::isClangOverlayOf(module, import)) {
reexposeImportedABI(module, import,
/*includeImportedModule=*/false);
}
}
SmallPtrSet<ModuleDecl *, 32> reexportedImports;
::getImmediateImports(module, reexportedImports,
{ModuleDecl::ImportFilterKind::Exported});
for (auto reexportedImport : reexportedImports) {
reexposeImportedABI(module, reexportedImport);
}
}
void ABIDependencyEvaluator::computeABIDependenciesForClangModule(
ModuleDecl *module) {
SmallPtrSet<ModuleDecl *, 32> imports;
::getImmediateImports(module, imports);
for (auto import : imports) {
// There are three cases here which can potentially create cycles:
//
// 1. Clang modules importing the stdlib.
// See [NOTE: Pure-Clang-modules-privately-import-stdlib].
// 2. Overlay S @_exported-imports underlying module S' and another Clang
// module C'. C' (transitively) #imports S' but it gets treated as if
// C' imports S. This creates a cycle: S -> C' -> ... -> S.
// In practice, this case is hit for
// Darwin (Swift) -> SwiftOverlayShims (Clang) -> Darwin (Swift).
// We may also hit this in a slightly different direction, in case
// the module directly imports SwiftOverlayShims:
// SwiftOverlayShims -> Darwin (Swift) -> SwiftOverlayShims
// The latter is handled later by isFakeCycleThroughOverlay.
// 3. [NOTE: Intra-module-leafwards-traversal]
// Cycles within the same top-level module.
// These don't matter for us, since we only care about the dependency
// graph at the granularity of top-level modules. So we ignore these
// by only considering parent -> submodule dependencies.
// See also [NOTE: Trace-Clang-submodule-complexity].
if (import->isStdlibModule()) {
continue;
}
if (!import->isNonSwiftModule() && isOverlayOfClangModule(import) &&
llvm::find(searchStack, import) != searchStack.end()) {
continue;
}
if (import->isNonSwiftModule() &&
module->getTopLevelModule() == import->getTopLevelModule() &&
(module == import ||
!import->findUnderlyingClangModule()->isSubModuleOf(
module->findUnderlyingClangModule()))) {
continue;
}
computeABIDependenciesForModule(import);
reexposeImportedABI(module, import);
}
}
void ABIDependencyEvaluator::getABIDependenciesForSwiftModule(
ModuleDecl *module, SmallPtrSetImpl<ModuleDecl *> &abiDependencies) {
computeABIDependenciesForModule(module);
SmallPtrSet<ModuleDecl *, 32> allImports;
::getImmediateImports(module, allImports);
for (auto directDependency : allImports) {
abiDependencies.insert(directDependency);
for (auto exposedDependency : abiExportMap[directDependency]) {
abiDependencies.insert(exposedDependency);
}
}
}
void ABIDependencyEvaluator::printModule(const ModuleDecl *module,
llvm::raw_ostream &os) {
module->getReverseFullModuleName().printForward(os);
os << (module->isNonSwiftModule() ? " (Clang)" : " (Swift)");
os << " @ " << llvm::format("0x%llx", reinterpret_cast<uintptr_t>(module));
}
template <typename SetLike>
void ABIDependencyEvaluator::printModuleSet(const SetLike &set,
llvm::raw_ostream &os) {
os << "{ ";
for (auto module : set) {
printModule(module, os);
os << ", ";
}
os << "}";
}
void ABIDependencyEvaluator::printABIExportMap(llvm::raw_ostream &os) const {
os << "ABI Export Map {{\n";
for (auto &entry : abiExportMap) {
printModule(entry.first, os);
os << " : ";
printModuleSet(entry.second, os);
os << "\n";
}
os << "}}\n";
}
/// Compute the per-module information to be recorded in the trace file.
//
// The most interesting/tricky thing here is _which_ paths get recorded in
// the trace file as dependencies. It depends on how the module was synthesized.
// The key points are:
//
// 1. Paths to swiftmodules in the module cache or in the prebuilt cache are not
// recorded - Precondition: the corresponding path to the swiftinterface must
// already be present as a key in pathToModuleDecl.
// 2. swiftmodules next to a swiftinterface are saved if they are up-to-date.
//
// FIXME: Use the VFS instead of handling paths directly. We are particularly
// sloppy about handling relative paths in the dependency tracker.
static void computeSwiftModuleTraceInfo(
const SmallPtrSetImpl<ModuleDecl *> &abiDependencies,
const llvm::DenseMap<StringRef, ModuleDecl *> &pathToModuleDecl,
const DependencyTracker &depTracker, StringRef prebuiltCachePath,
std::vector<SwiftModuleTraceInfo> &traceInfo) {
SmallString<256> buffer;
std::string errMsg;
llvm::raw_string_ostream err(errMsg);
// FIXME: Use PrettyStackTrace instead.
auto errorUnexpectedPath =
[&pathToModuleDecl](llvm::raw_string_ostream &errStream) {
errStream << "The module <-> path mapping we have is:\n";
for (auto &m : pathToModuleDecl)
errStream << m.second->getName() << " <-> " << m.first << '\n';
llvm::report_fatal_error(errStream.str());
};
using namespace llvm::sys;
auto computeAdjacentInterfacePath = [](SmallVectorImpl<char> &modPath) {
auto swiftInterfaceExt =
file_types::getExtension(file_types::TY_SwiftModuleInterfaceFile);
path::replace_extension(modPath, swiftInterfaceExt);
};
for (auto &depPath : depTracker.getDependencies()) {
// Decide if this is a swiftmodule based on the extension of the raw
// dependency path, as the true file may have a different one.
// For example, this might happen when the canonicalized path points to
// a Content Addressed Storage (CAS) location.
auto moduleFileType =
file_types::lookupTypeForExtension(path::extension(depPath));
auto isSwiftmodule = moduleFileType == file_types::TY_SwiftModuleFile;
auto isSwiftinterface =
moduleFileType == file_types::TY_SwiftModuleInterfaceFile;
if (!(isSwiftmodule || isSwiftinterface))
continue;
auto dep = pathToModuleDecl.find(depPath);
if (dep != pathToModuleDecl.end()) {
// Great, we recognize the path! Check if the file is still around.
ModuleDecl *depMod = dep->second;
if (depMod->isResilient() && !isSwiftinterface) {
// FIXME: Ideally, we would check that the swiftmodule has a
// swiftinterface next to it. Tracked by rdar://problem/56351399.
}
// FIXME: Better error handling
StringRef realDepPath =
fs::real_path(depPath, buffer, /*expand_tile*/ true)
? StringRef(depPath) // Couldn't find the canonical path, assume
// this is good enough.
: buffer.str();
bool isImportedDirectly = ::contains(abiDependencies, depMod);
traceInfo.push_back(
{/*Name=*/
depMod->getName(),
/*Path=*/
realDepPath.str(),
// TODO: There is an edge case which is not handled here.
// When we build a framework using -import-underlying-module, or an
// app/test using -import-objc-header, we should look at the direct
// imports of the bridging modules, and mark those as our direct
// imports.
// TODO: Add negative test cases for the comment above.
// TODO: Describe precise semantics of "isImportedDirectly".
/*IsImportedDirectly=*/
isImportedDirectly,
/*SupportsLibraryEvolution=*/
depMod->isResilient()});
buffer.clear();
continue;
}
// If the depTracker had an interface, that means that we must've
// built a swiftmodule from that interface, so we should have that
// filename available.
if (isSwiftinterface) {
err << "Unexpected path for swiftinterface file:\n" << depPath << "\n";
errorUnexpectedPath(err);
}
// Skip cached modules in the prebuilt cache. We will add the corresponding
// swiftinterface from the SDK directly, but this isn't checked. :-/
//
// FIXME: This is incorrect if both paths are not relative w.r.t. to the
// same root.
if (StringRef(depPath).startswith(prebuiltCachePath))
continue;
// If we have a swiftmodule next to an interface, that interface path will
// be saved (not checked), so don't save the path to this swiftmodule.
SmallString<256> moduleAdjacentInterfacePath(depPath);
computeAdjacentInterfacePath(moduleAdjacentInterfacePath);
if (::contains(pathToModuleDecl, moduleAdjacentInterfacePath))
continue;
// FIXME: The behavior of fs::exists for relative paths is undocumented.
// Use something else instead?
if (fs::exists(moduleAdjacentInterfacePath)) {
// This should be an error but it is not because of funkiness around
// compatible modules such as us having both armv7s.swiftinterface
// and armv7.swiftinterface in the dependency tracker.
continue;
}
buffer.clear();
// We might land here when we have a arm.swiftmodule in the cache path
// which added a dependency on a arm.swiftinterface (which was not loaded).
}
// Almost a re-implementation of reversePathSortedFilenames :(.
std::sort(traceInfo.begin(), traceInfo.end(),
[](const SwiftModuleTraceInfo &m1,
const SwiftModuleTraceInfo &m2) -> bool {
return std::lexicographical_compare(
m1.Path.rbegin(), m1.Path.rend(), m2.Path.rbegin(),
m2.Path.rend());
});
}
// [NOTE: Bailing-vs-crashing-in-trace-emission] There are certain edge cases
// in trace emission where an invariant that you think should hold does not hold
// in practice. For example, sometimes we have seen modules without any
// corresponding filename.
//
// Since the trace is a supplementary output for build system consumption, it
// it better to emit it on a best-effort basis instead of crashing and failing
// the build.
//
// Moreover, going forward, it would be nice if trace emission were more robust
// so we could emit the trace on a best-effort basis even if the dependency
// graph is ill-formed, so that the trace can be used as a debugging aid.
bool swift::emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule,
DependencyTracker *depTracker,
const FrontendOptions &opts,
const InputFile &input) {
ASTContext &ctxt = mainModule->getASTContext();
assert(!ctxt.hadError() &&
"We should've already exited earlier if there was an error.");
auto loadedModuleTracePath = input.getLoadedModuleTracePath();
if (loadedModuleTracePath.empty())
return false;
std::error_code EC;
llvm::raw_fd_ostream out(loadedModuleTracePath, EC, llvm::sys::fs::F_Append);
if (out.has_error() || EC) {
ctxt.Diags.diagnose(SourceLoc(), diag::error_opening_output,
loadedModuleTracePath, EC.message());
out.clear_error();
return true;
}
SmallPtrSet<ModuleDecl *, 32> abiDependencies;
{
ABIDependencyEvaluator evaluator{};
evaluator.getABIDependenciesForSwiftModule(mainModule, abiDependencies);
}
llvm::DenseMap<StringRef, ModuleDecl *> pathToModuleDecl;
for (const auto &module : ctxt.getLoadedModules()) {
ModuleDecl *loadedDecl = module.second;
if (!loadedDecl)
llvm::report_fatal_error("Expected loaded modules to be non-null.");
if (loadedDecl == mainModule)
continue;
if (loadedDecl->getModuleFilename().empty()) {
// FIXME: rdar://problem/59853077
// Ideally, this shouldn't happen. As a temporary workaround, avoid
// crashing with a message while we investigate the problem.
llvm::errs() << "WARNING: Module '" << loadedDecl->getName().str()
<< "' has an empty filename. This is probably an "
<< "invariant violation.\n"
<< "Please report it as a compiler bug.\n";
continue;
}
pathToModuleDecl.insert(
std::make_pair(loadedDecl->getModuleFilename(), loadedDecl));
}
std::vector<SwiftModuleTraceInfo> swiftModules;
computeSwiftModuleTraceInfo(abiDependencies, pathToModuleDecl, *depTracker,
opts.PrebuiltModuleCachePath, swiftModules);
LoadedModuleTraceFormat trace = {
/*version=*/LoadedModuleTraceFormat::CurrentVersion,
/*name=*/mainModule->getName(),
/*arch=*/ctxt.LangOpts.Target.getArchName().str(), swiftModules};
// raw_fd_ostream is unbuffered, and we may have multiple processes writing,
// so first write to memory and then dump the buffer to the trace file.
std::string stringBuffer;
{
llvm::raw_string_ostream memoryBuffer(stringBuffer);
json::Output jsonOutput(memoryBuffer, /*UserInfo=*/{},
/*PrettyPrint=*/false);
json::jsonize(jsonOutput, trace, /*Required=*/true);
}
stringBuffer += "\n";
out << stringBuffer;
return true;
}