Example #1
0
        private IPipGraphBuilder CreatePipGraphBuilder(
            LoggingContext loggingContext,
            MountsTable mountsTable,
            [CanBeNull] GraphReuseResult reuseResult)
        {
            var builder = new PipGraph.Builder(
                EngineSchedule.CreateEmptyPipTable(Context),
                Context,
                Scheduler.Tracing.Logger.Log,
                loggingContext,
                Configuration,
                mountsTable.MountPathExpander,
                fingerprintSalt: Configuration.Cache.CacheSalt,
                directoryMembershipFingerprinterRules: new Scheduler.DirectoryMembershipFingerprinterRuleSet(Configuration, Context.StringTable));

            PatchablePipGraph patchableGraph = null;

            if (Configuration.FrontEnd.UseGraphPatching() && reuseResult?.IsPartialReuse == true)
            {
                Logger.Log.UsingPatchableGraphBuilder(loggingContext);
                patchableGraph = new PatchablePipGraph(
                    oldPipGraph: reuseResult.PipGraph.DataflowGraph,
                    oldPipTable: reuseResult.PipGraph.PipTable,
                    graphBuilder: builder,
                    maxDegreeOfParallelism: Configuration.FrontEnd.MaxFrontEndConcurrency());
            }

            return((IPipGraphBuilder)patchableGraph ?? builder);
        }
        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));
        }
        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));
        }
Example #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));
        }
Example #5
0
        /// <summary>
        /// Loads configured symlink definitions (if not already loaded)
        /// Stores to cache for use by workers in distributed build
        /// Eagerly creates symlinks if lazy symlink creation is disabled
        /// </summary>
        public static async Task <Possible <SymlinkDefinitions> > TryPrepareSymlinkDefinitionsAsync(
            LoggingContext loggingContext,
            GraphReuseResult reuseResult,
            IConfiguration configuration,
            MasterService masterService,
            CacheInitializationTask cacheInitializerTask,
            PipExecutionContext context,
            ITempDirectoryCleaner tempDirectoryCleaner = null)
        {
            var  pathTable           = context.PathTable;
            bool isDistributedMaster = configuration.Distribution.BuildRole == DistributedBuildRoles.Master;
            Possible <SymlinkDefinitions> maybeSymlinkDefinitions = new Possible <SymlinkDefinitions>((SymlinkDefinitions)null);

            if (reuseResult?.IsFullReuse == true)
            {
                maybeSymlinkDefinitions = reuseResult.EngineSchedule.Scheduler.SymlinkDefinitions;
            }
            else if (configuration.Layout.SymlinkDefinitionFile.IsValid)
            {
                var symlinkFilePath = configuration.Layout.SymlinkDefinitionFile.ToString(pathTable);
                Logger.Log.SymlinkFileTraceMessage(loggingContext, I($"Loading symlink file from location '{symlinkFilePath}'."));
                maybeSymlinkDefinitions = await SymlinkDefinitions.TryLoadAsync(
                    loggingContext,
                    pathTable,
                    symlinkFilePath,
                    symlinksDebugPath : configuration.Logging.LogsDirectory.Combine(pathTable, "DebugSymlinksDefinitions.log").ToString(pathTable),
                    tempDirectoryCleaner : tempDirectoryCleaner);
            }

            if (!maybeSymlinkDefinitions.Succeeded || maybeSymlinkDefinitions.Result == null)
            {
                return(maybeSymlinkDefinitions);
            }

            // Need to store symlinks to cache for workers
            if (configuration.Distribution.BuildRole == DistributedBuildRoles.Master)
            {
                var possibleCacheInitializer = await cacheInitializerTask;
                if (!possibleCacheInitializer.Succeeded)
                {
                    return(possibleCacheInitializer.Failure);
                }

                Logger.Log.SymlinkFileTraceMessage(loggingContext, I($"Storing symlink file for use by workers."));

                var symlinkFile = configuration.Layout.SymlinkDefinitionFile.Expand(pathTable);

                var possibleStore = await TryStoreToCacheAsync(
                    loggingContext,
                    cache : possibleCacheInitializer.Result.CreateCacheForContext(context).ArtifactContentCache,
                    symlinkFile : symlinkFile);

                if (!possibleStore.Succeeded)
                {
                    return(possibleStore.Failure);
                }

                masterService.SymlinkFileContentHash = possibleStore.Result;
                Logger.Log.SymlinkFileTraceMessage(loggingContext, I($"Stored symlink file for use by workers."));
            }

            if (!configuration.Schedule.UnsafeLazySymlinkCreation || configuration.Engine.PopulateSymlinkDirectories.Count != 0)
            {
                // Symlink definition file is defined, and BuildXL intends to create it eagerly.
                // At this point master and worker should have had its symlink definition file, if specified.
                if (!FileContentManager.CreateSymlinkEagerly(loggingContext, configuration, pathTable, maybeSymlinkDefinitions.Result, context.CancellationToken))
                {
                    return(new Failure <string>("Failed eagerly creating symlinks"));
                }
            }

            return(maybeSymlinkDefinitions);
        }
Example #6
0
        /// <summary>
        /// Attempt to reuse the pip graph from a previous run
        /// </summary>
        private GraphReuseResult AttemptToReuseGraph(
            LoggingContext outerLoggingContext,
            int maxDegreeOfParallelism,
            GraphFingerprint graphFingerprint,
            IReadOnlyDictionary <string, string> properties,
            CacheInitializationTask cacheInitializationTask,
            JournalState journalState,
            EngineState engineState)
        {
            Contract.Ensures(Contract.Result <GraphReuseResult>() != null);

            GraphCacheCheckStatistics cacheGraphStats = CheckGraphCacheReuse(
                outerLoggingContext,
                maxDegreeOfParallelism,
                graphFingerprint,
                properties,
                cacheInitializationTask,
                journalState,
                out var serializer,
                out var inputChanges);

            // There are 3 cases in which we should reload the graph
            //   - we have a graph cache hit
            //   - the build is configured to reload the graph no matter what
            //   - graph patching is enabled and the reason for cache miss was 'SpecFileChanges'
            var shouldReload =
                cacheGraphStats.WasHit ||
                Configuration.Cache.CachedGraphPathToLoad.IsValid ||
                PartialReloadCondition(Configuration.FrontEnd, cacheGraphStats);

            if (!shouldReload)
            {
                return(GraphReuseResult.CreateForNoReuse(inputChanges));
            }

            bool fullReload = !PartialReloadCondition(Configuration.FrontEnd, cacheGraphStats);

            // Now we actually reload the graph
            var reloadStats = default(GraphCacheReloadStatistics);

            using (var tb = TimedBlock <EmptyStruct, GraphCacheReloadStatistics> .Start(
                       outerLoggingContext,
                       Statistics.GraphCacheReload,
                       (context, emptyStruct) =>
            {
                if (fullReload)
                {
                    Logger.Log.ReloadingPipGraphStart(context);
                }
                else
                {
                    Logger.Log.PartiallyReloadingEngineState(context);
                }
            },
                       default(EmptyStruct),
                       (context, graphCacheCheckStatistics) =>
            {
                Logger.Log.PartiallyReloadingEngineStateComplete(context, graphCacheCheckStatistics);
                m_enginePerformanceInfo.GraphReloadDurationMs = graphCacheCheckStatistics.ElapsedMilliseconds;
            },
                       () => reloadStats))
            {
                var reuseResult = fullReload
                    ? ReloadEngineSchedule(
                    serializer,
                    cacheInitializationTask,
                    journalState,
                    tb.LoggingContext,
                    engineState,
                    inputChanges,
                    graphFingerprint?.ExactFingerprint.BuildEngineHash.ToString())
                    : ReloadPipGraphOnly(serializer, tb.LoggingContext, engineState, inputChanges);

                // Set telemetry statistics
                reloadStats.SerializedFileSizeBytes = serializer.BytesDeserialized;
                reloadStats.Success = !reuseResult.IsNoReuse;

                return(reuseResult);
            }
        }
Example #7
0
        private bool ConstructAndEvaluateGraph(
            LoggingContext loggingContext,
            FrontEndEngineAbstraction frontEndEngineAbstration,
            CacheInitializationTask engineCacheTask,
            MountsTable mountsTable,
            EvaluationFilter evaluationFilter,
            [CanBeNull] GraphReuseResult reuseResult,
            out PipGraph pipGraph)
        {
            Contract.Requires(frontEndEngineAbstration != null);
            Contract.Requires(engineCacheTask != null);
            Contract.Requires(mountsTable != null);

            pipGraph = null;
            IPipGraphBuilder pipGraphBuilder = null;

            if (!AddConfigurationMountsAndCompleteInitialization(loggingContext, mountsTable))
            {
                return(false);
            }

            IDictionary <ModuleId, MountsTable> moduleMountsTableMap;

            if (!mountsTable.PopulateModuleMounts(Configuration.ModulePolicies.Values, out moduleMountsTableMap))
            {
                Contract.Assume(loggingContext.ErrorWasLogged, "An error should have been logged after MountTable.PopulateModuleMounts()");
                return(false);
            }

            m_visualization?.MountsTable.MakeAvailable(mountsTable);

            if ((Configuration.Engine.Phase & EnginePhases.Schedule) != 0)
            {
                pipGraphBuilder = CreatePipGraphBuilder(loggingContext, mountsTable, reuseResult);
            }

            // Have to do some horrible magic here to get to a proper Task<T> with the BuildXL cache since
            // someone updated the engine cache to be an await style pattern, and there is no way to get to the EngineCache
            // If the cache was fast to startup, but perhaps blocked itself on first access we wouldn't have to do all these hoops.
            Func <Task <Possible <EngineCache> > > getBuildCacheTask =
                async() =>
            {
                return((await engineCacheTask).Then(engineCache => engineCache.CreateCacheForContext()));
            };

            if (!FrontEndController.PopulateGraph(
                    getBuildCacheTask(),
                    pipGraphBuilder,
                    frontEndEngineAbstration,
                    evaluationFilter,
                    Configuration,
                    m_initialCommandLineConfiguration.Startup))
            {
                LogFrontEndStats(loggingContext);

                Contract.Assume(loggingContext.ErrorWasLogged, "An error should have been logged after FrontEndController.PopulateGraph()");
                return(false);
            }

            LogFrontEndStats(loggingContext);

            // Pip graph must become immutable now that evaluation is done (required to construct a scheduler).
            return(pipGraphBuilder == null || (pipGraph = pipGraphBuilder.Build()) != null);
        }