예제 #1
0
        private GraphReuseResult ReloadEngineSchedule(
            EngineSerializer serializer,
            CacheInitializationTask cacheInitializationTask,
            JournalState journalState,
            LoggingContext loggingContext,
            EngineState engineState,
            InputTracker.InputChanges inputChanges,
            string buildEngineFingerprint)
        {
            Tuple <EngineSchedule, EngineContext, IConfiguration> t = null;

            try
            {
                t = EngineSchedule.LoadAsync(
                    Context,
                    serializer,
                    cacheInitializationTask,
                    FileContentTable,
                    journalState,
                    Configuration,
                    loggingContext,
                    m_collector,
                    m_directoryTranslator,
                    engineState,
                    tempCleaner: m_tempCleaner,
                    buildEngineFingerprint).GetAwaiter().GetResult();
            }
            catch (BuildXLException e)
            {
                Logger.Log.FailedReloadPipGraph(loggingContext, e.ToString());
            }

            if (t == null)
            {
                return(GraphReuseResult.CreateForNoReuse(inputChanges));
            }

            // Invalidate the old context to ensure nothing uses it anymore
            // Update engine state to deserialized state
            Context.Invalidate();
            Context = t.Item2;

            // Update configuration state to deserialized state
            Configuration = t.Item3;

            // Copy the graph files to the session output
            if (Configuration.Distribution.BuildRole != DistributedBuildRoles.Worker)
            {
                // No need to link these files to the logs directory on workers since they are redundant with what's on the orchestrator
                m_executionLogGraphCopy  = TryCreateHardlinksToScheduleFilesInSessionFolder(loggingContext, serializer);
                m_previousInputFilesCopy = TryCreateHardlinksToPreviousInputFilesInSessionFolder(loggingContext, serializer);
            }

            return(GraphReuseResult.CreateForFullReuse(t.Item1, inputChanges));
        }
예제 #2
0
        private GraphReuseResult ReloadPipGraphOnly(
            EngineSerializer serializer,
            LoggingContext loggingContext,
            EngineState engineState,
            InputTracker.InputChanges inputChanges)
        {
            Tuple <PipGraph, EngineContext> t = null;

            try
            {
                t = EngineSchedule.LoadPipGraphAsync(
                    Context,
                    serializer,
                    Configuration,
                    loggingContext,
                    engineState).GetAwaiter().GetResult();
            }
            catch (BuildXLException e)
            {
                Logger.Log.FailedReloadPipGraph(loggingContext, e.ToString());
            }

            if (t == null)
            {
                return(GraphReuseResult.CreateForNoReuse(inputChanges));
            }

            var newContext = t.Item2;

            if (!ShouldReuseReloadedEngineContextGivenHistoricData(loggingContext, newContext.NextHistoricTableSizes))
            {
                return(GraphReuseResult.CreateForNoReuse(inputChanges));
            }

            var newPathTable = newContext.PathTable;
            var pathRemapper = new PathRemapper(Context.PathTable, newPathTable);

            Configuration = new ConfigurationImpl(Configuration, pathRemapper);

            m_initialCommandLineConfiguration = new CommandLineConfiguration(m_initialCommandLineConfiguration, pathRemapper);

            // Invalidate the old context to ensure nothing uses it anymore
            // Update engine state to deserialized state
            Context.Invalidate();
            Context = newContext;

            // Additionally recreate front end controller, because the old one uses the invalidated context.
            //   - to fully initialize the front end, we have to go through all the steps that have already been
            //     executed on the old controller; those steps are (1) InitializeHost, and (2) ParseConfig
            FrontEndController = m_frontEndControllerFactory.Create(Context.PathTable, Context.SymbolTable);
            FrontEndController.InitializeHost(Context.ToFrontEndContext(loggingContext), m_initialCommandLineConfiguration);
            FrontEndController.ParseConfig(m_initialCommandLineConfiguration);

            return(GraphReuseResult.CreateForPartialReuse(t.Item1, inputChanges));
        }
예제 #3
0
        /// <summary>
        /// Class constructor
        /// </summary>
        public CachedGraph(PipGraph pipGraph, IReadonlyDirectedGraph directedGraph, PipExecutionContext context, MountPathExpander mountPathExpander, EngineSerializer serializer = null)
        {
            Contract.Requires(pipGraph != null);
            Contract.Requires(directedGraph != null);
            Contract.Requires(context != null);
            Contract.Requires(mountPathExpander != null);

            DirectedGraph     = directedGraph;
            PipTable          = pipGraph.PipTable;
            MountPathExpander = mountPathExpander;
            Context           = context;
            PipGraph          = pipGraph;
            Serializer        = serializer;
        }
예제 #4
0
        private GraphReuseResult ReloadEngineSchedule(
            EngineSerializer serializer,
            CacheInitializationTask cacheInitializationTask,
            JournalState journalState,
            LoggingContext loggingContext,
            EngineState engineState,
            InputTracker.InputChanges inputChanges,
            string buildEngineFingerprint)
        {
            Tuple <EngineSchedule, EngineContext, IConfiguration> t = EngineSchedule.LoadAsync(
                Context,
                serializer,
                cacheInitializationTask,
                FileContentTable,
                journalState,
                Configuration,
                loggingContext,
                m_collector,
                m_directoryTranslator,
                engineState,
                symlinkDefinitionFile: IsDistributedWorker ?
                m_workerSymlinkDefinitionFile.Value :
                Configuration.Layout.SymlinkDefinitionFile,
                tempCleaner: m_tempCleaner,
                buildEngineFingerprint).GetAwaiter().GetResult();

            if (t == null)
            {
                return(GraphReuseResult.CreateForNoReuse(inputChanges));
            }

            // Invalidate the old context to ensure nothing uses it anymore
            // Update engine state to deserialized state
            Context.Invalidate();
            Context = t.Item2;

            // Update configuration state to deserialized state
            Configuration = t.Item3;

            // Copy the graph files to the session output
            m_executionLogGraphCopy  = TryCreateHardlinksToScheduleFilesInSessionFolder(loggingContext, serializer);
            m_previousInputFilesCopy = TryCreateHardlinksToPreviousInputFilesInSessionFolder(loggingContext, serializer);

            return(GraphReuseResult.CreateForFullReuse(t.Item1, inputChanges));
        }
예제 #5
0
 private static Lazy <Task <T> > CreateLazyFileDeserialization <T>(EngineSerializer serializer, GraphCacheFile file, Func <BuildXLReader, Task <T> > deserializer)
 {
     return(CreateAsyncLazy(() => serializer.DeserializeFromFileAsync <T>(file, deserializer)));
 }
예제 #6
0
        private CachedGraphLoader(CancellationToken cancellationToken, EngineSerializer serializer)
        {
            Serializer       = serializer;
            m_pipGraphIdTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.PipGraph,
                async reader =>
            {
                var graphId = await PipGraph.DeserializeGraphIdAsync(reader);
                return(graphId.Item1);
            });

            var directedGraphTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.DirectedGraph,
                DeserializedDirectedGraph.DeserializeAsync);

            m_stringTableTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.StringTable,
                StringTable.DeserializeAsync);

            m_pathTableTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.PathTable,
                reader => reader.ReadPathTableAsync(m_stringTableTask.Value));

            m_symbolTableTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.SymbolTable,
                reader => reader.ReadSymbolTableAsync(m_stringTableTask.Value));

            m_qualifierTableTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.QualifierTable,
                reader => reader.ReadQualifierTableAsync(m_stringTableTask.Value));

            m_pipTableTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.PipTable,
                (reader) => PipTable.DeserializeAsync(
                    reader,
                    m_pathTableTask.Value,
                    m_symbolTableTask.Value,
                    EngineSchedule.PipTableInitialBufferSize,
                    EngineSchedule.PipTableMaxDegreeOfParallelismDuringConstruction,
                    debug: false));

            m_mountPathExpanderTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.MountPathExpander,
                reader => MountPathExpander.DeserializeAsync(reader, m_pathTableTask.Value));

            m_pipExecutionContextTask = CreateAsyncLazy(() => Task.Run(
                                                            async() =>
            {
                var stringTable    = await m_stringTableTask.Value;
                var pathTable      = await m_pathTableTask.Value;
                var symbolTable    = await m_symbolTableTask.Value;
                var qualifierTable = await m_qualifierTableTask.Value;

                if (stringTable != null && pathTable != null && symbolTable != null && qualifierTable != null)
                {
                    return((PipExecutionContext) new SchedulerContext(cancellationToken, stringTable, pathTable, symbolTable, qualifierTable));
                }

                return(null);
            }));

            m_historicDataTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.HistoricTableSizes,
                reader => Task.FromResult(HistoricTableSizes.Deserialize(reader)));

            m_pipGraphTask = CreateLazyFileDeserialization(
                serializer,
                GraphCacheFile.PipGraph,
                reader => PipGraph.DeserializeAsync(
                    reader,
                    serializer.LoggingContext,
                    m_pipTableTask.Value,
                    directedGraphTask.Value,
                    m_pipExecutionContextTask.Value,
                    ToSemanticPathExpander(m_mountPathExpanderTask.Value)));

            m_cachedGraphTask = CreateAsyncLazy(() => CreateCachedGraph(m_pipTableTask.Value, m_pipGraphTask.Value, directedGraphTask.Value, m_pipExecutionContextTask.Value, m_mountPathExpanderTask.Value));
        }
예제 #7
0
 /// <summary>
 /// Loads a cache graph with a given serializer
 /// </summary>
 public static CachedGraphLoader CreateFromDisk(CancellationToken cancellationToken, EngineSerializer serializer)
 {
     return(new CachedGraphLoader(cancellationToken, serializer));
 }
예제 #8
0
        /// <summary>
        /// Loads a cache graph from a given cached graph directory
        /// </summary>
        public static CachedGraphLoader CreateFromDisk(CancellationToken cancellationToken, string cachedGraphDirectory, LoggingContext loggingContext, FileSystemStreamProvider readStreamProvider = null)
        {
            var serializer = new EngineSerializer(loggingContext, cachedGraphDirectory, readOnly: true, readStreamProvider: readStreamProvider);

            return(new CachedGraphLoader(cancellationToken, serializer));
        }
예제 #9
0
        /// <summary>
        /// Check if pip graph can be reused.
        ///
        /// There are 3 opportunities to determine a graph match. The applicability of each depends on the distributed build roles.
        ///   (1) from engine cache,
        ///   (2) from content cache, and
        ///   (3) from master node (if running on a worker node in a distributed build)
        /// </summary>
        private GraphCacheCheckStatistics CheckGraphCacheReuse(
            LoggingContext outerLoggingContext,
            int maxDegreeOfParallelism,
            GraphFingerprint graphFingerprint,
            IReadOnlyDictionary <string, string> properties,
            CacheInitializationTask cacheInitializationTask,
            JournalState journalState,
            out EngineSerializer serializer,
            out InputTracker.InputChanges inputChanges)
        {
            serializer   = CreateEngineSerializer(outerLoggingContext);
            inputChanges = null;
            var cacheGraphStats = default(GraphCacheCheckStatistics);

            using (var timeBlock = TimedBlock <EmptyStruct, GraphCacheCheckStatistics> .Start(
                       outerLoggingContext,
                       Statistics.GraphCacheReuseCheck,
                       (context, emptyStruct) => Logger.Log.CheckingForPipGraphReuseStart(context),
                       default(EmptyStruct),
                       (loggingContext, stats) =>
            {
                Logger.Log.CheckingForPipGraphReuseComplete(loggingContext, stats);

                // On misses we want to give the user a message for why there was a miss
                if (!stats.WasHit)
                {
                    Contract.Assume(stats.MissReason != GraphCacheMissReason.NoMiss);
                    Logger.Log.GraphNotReusedDueToChangedInput(loggingContext, stats.MissMessageForConsole, stats.MissDescription);
                }

                m_enginePerformanceInfo.GraphCacheCheckDurationMs = stats.ElapsedMilliseconds;
                m_enginePerformanceInfo.GraphCacheCheckJournalEnabled = stats.JournalEnabled;
            },
                       () => cacheGraphStats))
            {
                var loggingContext = timeBlock.LoggingContext;
                var effectiveEnvironmentVariables = FrontEndEngineImplementation.PopulateFromEnvironmentAndApplyOverrides(properties);
                var availableMounts = MountsTable.CreateAndRegister(loggingContext, Context, Configuration, m_initialCommandLineConfiguration.Startup.Properties);

                if (!AddConfigurationMountsAndCompleteInitialization(loggingContext, availableMounts))
                {
                    return(cacheGraphStats);
                }

                cacheGraphStats.JournalEnabled = journalState.IsEnabled;

                // ************************************************************
                // 1. Engine cache check:
                // ************************************************************
                // * Single machine builds
                // Distributed builds rely on the graph being available via the cache for it to be shared between master
                // and workers. So even if the master could have had a hit from the engine cache, it must be ignored
                // since the workers would not be able to retrieve it.
                if (!HasExplicitlyLoadedGraph(Configuration.Cache) &&
                    !Configuration.Schedule.ForceUseEngineInfoFromCache &&
                    Configuration.Distribution.BuildRole == DistributedBuildRoles.None)
                {
                    Contract.Assume(
                        graphFingerprint != null,
                        "When looking up a cached graph on a distributed master or single-machine build, a graph fingerprint must be computed");

                    InputTracker.MatchResult engineCacheMatchResult = CheckIfAvailableInputsToGraphMatchPreviousRun(
                        loggingContext,
                        serializer,
                        graphFingerprint: graphFingerprint,
                        availableEnvironmentVariables: effectiveEnvironmentVariables,
                        availableMounts: availableMounts,
                        journalState: journalState,
                        maxDegreeOfParallelism: maxDegreeOfParallelism);
                    cacheGraphStats.ObjectDirectoryHit        = engineCacheMatchResult.Matches;
                    cacheGraphStats.ObjectDirectoryMissReason = engineCacheMatchResult.MissType;
                    cacheGraphStats.MissReason        = engineCacheMatchResult.MissType;
                    cacheGraphStats.MissDescription   = engineCacheMatchResult.FirstMissIdentifier;
                    cacheGraphStats.WasHit            = engineCacheMatchResult.Matches;
                    cacheGraphStats.InputFilesChecked = engineCacheMatchResult.FilesChecked;

                    // Checking the engine cache may have used a FileChangeTracker and provided information about
                    // files/ContentHash pairs that were unchanged from the previous run. Hold onto this information as it may
                    // be useful when eventually parsing files.
                    // The FileChangeTracker is now up to date. All changed files have been removed from it. It can be reused
                    // for a future build with the same fingerprint, though it may be tracking extra files, for example if
                    // a spec was removed since the previous build.
                    inputChanges = engineCacheMatchResult.InputChanges;
                }

                var shouldTryContentCache =
                    !cacheGraphStats.WasHit &&
                    Configuration.Distribution.BuildRole != DistributedBuildRoles.Worker &&
                    Configuration.Cache.AllowFetchingCachedGraphFromContentCache &&
                    !HasExplicitlyLoadedGraph(Configuration.Cache) &&
                    (!Configuration.FrontEnd.UseSpecPublicFacadeAndAstWhenAvailable.HasValue ||
                     !Configuration.FrontEnd.UseSpecPublicFacadeAndAstWhenAvailable.Value);

                // ************************************************************
                // 2. Content cache check:
                // ************************************************************
                // * Single machine builds that missed earlier
                // * Distributed masters
                // This is the only valid place for the master to get a hit since it must be in the cache for the
                // workers to get it.
                if (shouldTryContentCache)
                {
                    // Since an in-place match did not succeed, we need a readied cache to try again.
                    // TODO: This logs an error if it fails. We are assuming some later thing will ensure that, if failed,
                    //       the engine fails overall.
                    Possible <CacheInitializer> possibleCacheInitializerForFallback = cacheInitializationTask.GetAwaiter().GetResult();
                    if (possibleCacheInitializerForFallback.Succeeded)
                    {
                        CacheInitializer cacheInitializerForFallback = possibleCacheInitializerForFallback.Result;
                        using (EngineCache cacheForFallback = cacheInitializerForFallback.CreateCacheForContext())
                        {
                            var cacheGraphProvider = new CachedGraphProvider(
                                loggingContext,
                                Context,
                                cacheForFallback,
                                FileContentTable,
                                maxDegreeOfParallelism);

                            var cachedGraphDescriptor =
                                cacheGraphProvider.TryGetPipGraphCacheDescriptorAsync(graphFingerprint, effectiveEnvironmentVariables, availableMounts.MountsByName).Result;

                            if (cachedGraphDescriptor == null)
                            {
                                // There was no matching fingerprint in the cache. Record the status for logging before returning.
                                cacheGraphStats.CacheMissReason = GraphCacheMissReason.FingerprintChanged;
                                SetMissReasonIfUnset(ref cacheGraphStats, cacheGraphStats.CacheMissReason);
                                return(cacheGraphStats);
                            }

                            var fetchEngineScheduleContent = EngineSchedule.TryFetchFromCacheAsync(
                                loggingContext,
                                Context,
                                cacheForFallback,
                                cachedGraphDescriptor,
                                serializer,
                                FileContentTable,
                                m_tempCleaner).Result;

                            if (!fetchEngineScheduleContent)
                            {
                                cacheGraphStats.CacheMissReason = GraphCacheMissReason.NoPreviousRunToCheck;
                                SetMissReasonIfUnset(ref cacheGraphStats, cacheGraphStats.CacheMissReason);
                                return(cacheGraphStats);
                            }

                            // If a distributed master, take note of the graph fingerprint
                            if (Configuration.Distribution.BuildRole == DistributedBuildRoles.Master)
                            {
                                Contract.Assert(cachedGraphDescriptor != null);
                                m_masterService.CachedGraphDescriptor = cachedGraphDescriptor;
                            }

                            Logger.Log.FetchedSerializedGraphFromCache(outerLoggingContext);

                            cacheGraphStats.CacheMissReason = GraphCacheMissReason.NoMiss;
                            cacheGraphStats.MissReason      = cacheGraphStats.CacheMissReason;
                            cacheGraphStats.WasHit          = true;
                        }
                    }
                    else
                    {
                        cacheGraphStats.CacheMissReason = GraphCacheMissReason.CacheFailure;
                        SetMissReasonIfUnset(ref cacheGraphStats, cacheGraphStats.CacheMissReason);
                        return(cacheGraphStats);
                    }
                }

                // ************************************************************
                // 3. Query distributed master
                // ************************************************************
                // * Distributed workers only
                if (Configuration.Distribution.BuildRole == DistributedBuildRoles.Worker)
                {
                    Contract.Assume(
                        graphFingerprint == null,
                        "Distributed workers should request a graph fingerprint from the master (not compute one locally)");
                    Possible <CacheInitializer> possibleCacheInitializerForWorker = cacheInitializationTask.GetAwaiter().GetResult();
                    Contract.Assume(possibleCacheInitializerForWorker.Succeeded, "Workers must have a valid cache");
                    CacheInitializer cacheInitializerForWorker = possibleCacheInitializerForWorker.Result;

                    using (EngineCache cacheForWorker = cacheInitializerForWorker.CreateCacheForContext())
                    {
                        PipGraphCacheDescriptor schedulerStateDescriptor;
                        if (!m_workerService.TryGetBuildScheduleDescriptor(out schedulerStateDescriptor) ||
                            !EngineSchedule.TryFetchFromCacheAsync(
                                outerLoggingContext,
                                Context,
                                cacheForWorker,
                                schedulerStateDescriptor,
                                serializer,
                                FileContentTable,
                                m_tempCleaner).Result)
                        {
                            cacheGraphStats.CacheMissReason = GraphCacheMissReason.NoFingerprintFromMaster;
                            cacheGraphStats.MissReason      = cacheGraphStats.CacheMissReason;
                            return(cacheGraphStats);
                        }

                        AsyncOut <AbsolutePath> symlinkFileLocation = new AsyncOut <AbsolutePath>();
                        if (!SymlinkDefinitionFileProvider.TryFetchWorkerSymlinkFileAsync(
                                outerLoggingContext,
                                Context.PathTable,
                                cacheForWorker,
                                Configuration.Layout,
                                m_workerService,
                                symlinkFileLocation).Result)
                        {
                            cacheGraphStats.CacheMissReason = GraphCacheMissReason.NoFingerprintFromMaster;
                            cacheGraphStats.MissReason      = cacheGraphStats.CacheMissReason;
                            return(cacheGraphStats);
                        }

                        m_workerSymlinkDefinitionFile = symlinkFileLocation.Value;

                        // Success. Populate the stats
                        cacheGraphStats.WasHit          = true;
                        cacheGraphStats.WorkerHit       = true;
                        cacheGraphStats.MissReason      = GraphCacheMissReason.NoMiss;
                        cacheGraphStats.CacheMissReason = GraphCacheMissReason.NoMiss;
                    }
                }
            }

            return(cacheGraphStats);
        }