Loading...
cache_builder/mrm_shared_cache_builder.cpp dyld-1122.1 dyld-1162
--- dyld/dyld-1122.1/cache_builder/mrm_shared_cache_builder.cpp
+++ dyld/dyld-1162/cache_builder/mrm_shared_cache_builder.cpp
@@ -51,7 +51,8 @@
 
 struct BuildInstance {
     std::unique_ptr<cache_builder::BuilderOptions>  options;
-    std::unique_ptr<SharedCacheBuilder>             builder;
+    std::vector<cache_builder::FileAlias>           aliases;
+    std::vector<cache_builder::FileAlias>           intermediateAliases;
     std::string                                     mainCacheFilePath;
     std::vector<const char*>                        errors;
     std::vector<const char*>                        warnings;
@@ -412,8 +413,10 @@
 
 static cache_builder::CacheKind getCacheKind(const BuildOptions_v1* options)
 {
-    // Work out what kind of cache we are building.  macOS/driverKit are always development
-    if ( (options->platform == macOS) || (options->platform == driverKit) )
+    // Work out what kind of cache we are building.  macOS/driverKit/exclaveKit are always development
+    if (   (options->platform == macOS)
+        || (options->platform == driverKit)
+        || dyld3::MachOFile::isExclaveKitPlatform((dyld3::Platform)options->platform) )
         return cache_builder::CacheKind::development;
 
     // Sims are always development
@@ -500,11 +503,13 @@
         if ( strcmp(builder->options->archs[i], "i386") == 0 )
             continue;
 
-        // Add a driverKit suffix.  Note we don't need to add .development suffixes any
+        // Add a driverKit/exclaveKit suffix.  Note we don't need to add .development suffixes any
         // more as the universal caches don't build customer and development seperately
         const char *loggingSuffix = "";
         if ( builder->options->platform == Platform::driverKit )
             loggingSuffix = ".driverKit";
+        if ( dyld3::MachOFile::isExclaveKitPlatform((dyld3::Platform)builder->options->platform) )
+            loggingSuffix = ".exclaveKit";
 
         std::string loggingPrefix = "";
         loggingPrefix += std::string(builder->options->deviceName);
@@ -521,6 +526,8 @@
                 runtimePath = MACOSX_MRM_DYLD_SHARED_CACHE_DIR;
             else if ( builder->options->platform == Platform::driverKit )
                 runtimePath = DRIVERKIT_DYLD_SHARED_CACHE_DIR;
+            else if ( dyld3::MachOFile::isExclaveKitPlatform((dyld3::Platform)builder->options->platform) )
+                runtimePath = EXCLAVEKIT_DYLD_SHARED_CACHE_DIR;
             else
                 runtimePath = IPHONE_DYLD_SHARED_CACHE_DIR;
             runtimePath = runtimePath + "dyld_shared_cache_" + builder->options->archs[i];
@@ -556,40 +563,12 @@
         options->localSymbolsMode            = excludeLocalSymbols(builder->options);
 
         BuildInstance buildInstance;
-        buildInstance.options           = std::move(options);
-        buildInstance.builder           = std::make_unique<SharedCacheBuilder>(*buildInstance.options.get(), builder->fileSystem);
-        buildInstance.mainCacheFilePath = runtimePath;
+        buildInstance.options               = std::move(options);
+        buildInstance.aliases               = aliases;
+        buildInstance.intermediateAliases   = intermediateAliases;
+        buildInstance.mainCacheFilePath     = runtimePath;
 
         builder->builders.push_back(std::move(buildInstance));
-    }
-
-    // Add all the input files
-    __block std::vector<cache_builder::InputFile> inputFiles;
-    builder->fileSystem.forEachFileInfo(^(const char* path, const void* buffer, size_t bufferSize,
-                                          FileFlags fileFlags, uint64_t inode, uint64_t modTime) {
-        switch (fileFlags) {
-            case FileFlags::NoFlags:
-            case FileFlags::MustBeInCache:
-            case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf:
-            case FileFlags::RequiredClosure:
-                break;
-            case FileFlags::DylibOrderFile:
-            case FileFlags::DirtyDataOrderFile:
-            case FileFlags::ObjCOptimizationsFile:
-                builder->error("Order files should not be in the file system");
-                return;
-        }
-
-        for (auto& buildInstance : builder->builders) {
-            SharedCacheBuilder* cacheBuilder = buildInstance.builder.get();
-            cacheBuilder->addFile(buffer, bufferSize, path, inode, modTime);
-        }
-    });
-
-    // Add resolved aliases (symlinks)
-    for (auto& buildInstance : builder->builders) {
-        SharedCacheBuilder* cacheBuilder = buildInstance.builder.get();
-        cacheBuilder->setAliases(aliases, intermediateAliases);
     }
 
     return true;
@@ -598,14 +577,65 @@
 static void runBuilders(struct MRMSharedCacheBuilder* builder)
 {
     for (auto& buildInstance : builder->builders) {
-        std::unique_ptr<SharedCacheBuilder> cacheBuilder = std::move(buildInstance.builder);
-        Error error = cacheBuilder->build();
+        // The build might overflow, so loop until we don't error from overflow
+        std::vector<std::string> evictedDylibs;
+        std::unordered_set<std::string> evictedDylibsSet;
+        __block std::unique_ptr<SharedCacheBuilder> cacheBuilder;
+        Error error;
+        while ( true ) {
+            cacheBuilder = std::make_unique<SharedCacheBuilder>(*buildInstance.options.get(), builder->fileSystem);
+
+            // Add all the input files
+            __block std::vector<cache_builder::InputFile> inputFiles;
+            builder->fileSystem.forEachFileInfo(^(const char* path, const void* buffer, size_t bufferSize,
+                                                  FileFlags fileFlags, uint64_t inode, uint64_t modTime) {
+                switch (fileFlags) {
+                    case FileFlags::NoFlags:
+                    case FileFlags::MustBeInCache:
+                    case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf:
+                    case FileFlags::RequiredClosure:
+                        break;
+                    case FileFlags::DylibOrderFile:
+                    case FileFlags::DirtyDataOrderFile:
+                    case FileFlags::ObjCOptimizationsFile:
+                        builder->error("Order files should not be in the file system");
+                        return;
+                }
+
+                cacheBuilder->addFile(buffer, bufferSize, path, inode, modTime, evictedDylibsSet.count(path));
+            });
+
+            // Add resolved aliases (symlinks)
+            cacheBuilder->setAliases(buildInstance.aliases, buildInstance.intermediateAliases);
+
+            error = cacheBuilder->build();
+
+            // Get result buffers, even if there's an error.  That way we'll free them
+            cacheBuilder->getResults(buildInstance.cacheBuffers);
+
+            if ( !error.hasError() )
+                break;
+
+            // We have an error. If its cache overflow, then we can try again, with some evicted dylibs
+            std::span<const std::string_view> newEvictedDylibs = cacheBuilder->getEvictedDylibs();
+            if ( newEvictedDylibs.empty() ) {
+                // Error wasn't eviction, so something else. Break out an handle it as a fatal error
+                break;
+            }
+
+            // Cache eviction happened. Note down the bad dylibs, and try again
+            // Note we should never have buffer data to free at this point as eviction should be
+            // determined before buffers are allocated
+            for ( const CacheBuffer& buffer : buildInstance.cacheBuffers ) {
+                assert(buffer.bufferData == nullptr);
+            }
+            buildInstance.cacheBuffers.clear();
+            evictedDylibs.insert(evictedDylibs.end(), newEvictedDylibs.begin(), newEvictedDylibs.end());
+            evictedDylibsSet.insert(newEvictedDylibs.begin(), newEvictedDylibs.end());
+        }
 
         buildInstance.loggingPrefix = cacheBuilder->developmentLoggingPrefix();
         buildInstance.customerLoggingPrefix = cacheBuilder->customerLoggingPrefix();
-
-        // Get result buffers, even if there's an error.  That way we'll free them
-        cacheBuilder->getResults(buildInstance.cacheBuffers);
 
         // Track all buffers to be freed/unmapped (see allocateSubCacheBuffers() for allocation)
         for ( const CacheBuffer& buffer : buildInstance.cacheBuffers ) {
@@ -634,6 +664,12 @@
 
             // First put the warnings in to a vector to own them.
             if ( builder->options->verboseDiagnostics ) {
+                // Add cache eviction warnings, if any
+                for ( std::string path : evictedDylibs ) {
+                    std::string reason = "Dylib located at '" + path + "' not placed in shared cache because: cache overflow";
+                    buildInstance.warningStrings.push_back(reason);
+                }
+
                 cacheBuilder->forEachWarning(^(const std::string_view &str) {
                     buildInstance.warningStrings.push_back(std::string(str));
                 });
@@ -675,6 +711,13 @@
 
             // Only add warnings if the build was good
             // First put the warnings in to a vector to own them.
+
+            // Add cache eviction warnings, if any
+            for ( std::string path : evictedDylibs ) {
+                std::string reason = "Dylib located at '" + path + "' not placed in shared cache because: cache overflow";
+                buildInstance.warningStrings.push_back(reason);
+            }
+
             cacheBuilder->forEachWarning(^(const std::string_view &str) {
                 buildInstance.warningStrings.push_back(std::string(str));
             });
@@ -827,6 +870,10 @@
 
 static void calculateDylibsToDelete(struct MRMSharedCacheBuilder* builder)
 {
+    // Keep ExclaveKit binaries on disk, remove that exception later (rdar://112851136)
+    if ( dyld3::MachOFile::isExclaveKitPlatform((dyld3::Platform)builder->options->platform) )
+        return;
+
     // Add entries to tell us to remove all of the dylibs from disk which are in every cache.
     const size_t numCaches = builder->builders.size();
     for (const auto& dylibAndCount : builder->dylibsInCaches) {