/// <summary> /// Creates a new change tracker in the <see cref="FileChangeTrackingState.BuildingInitialChangeTrackingSet"/> state. /// The caller may then add tracking for full set of files of interest, for later re-use by /// <see cref="ResumeTrackingChanges(LoggingContext,BuildXL.Utilities.FileEnvelopeId,VolumeMap,IChangeJournalAccessor,FileChangeTrackingSet,string)"/>. /// </summary> public static FileChangeTracker StartTrackingChanges( LoggingContext loggingContext, VolumeMap volumeMap, IChangeJournalAccessor journal, FileChangeTrackerSupersedeMode supersedeMode, string buildEngineFingerprint, FileEnvelopeId?correlatedId = default) { Contract.Requires(loggingContext != null); Contract.Requires(volumeMap != null); Contract.Requires(journal != null); Contract.Ensures(Contract.Result <FileChangeTracker>().TrackingState == FileChangeTrackingState.BuildingInitialChangeTrackingSet); var tracker = new FileChangeTracker( loggingContext, correlatedId ?? FileEnvelopeId.Create(), FileChangeTrackingState.BuildingInitialChangeTrackingSet, volumeMap, journal, FileChangeTrackingSet.CreateForAllCapableVolumes(loggingContext, volumeMap, journal, supersedeMode), buildEngineFingerprint); foreach (var gvfsProjectionFile in volumeMap.GvfsProjections) { var maybeTracking = tracker.TryProbeAndTrackPath(gvfsProjectionFile); if (!maybeTracking.Succeeded) { Logger.Log.TrackChangesToGvfsProjectionFailed(loggingContext, gvfsProjectionFile, maybeTracking.Failure.DescribeIncludingInnerFailures()); } } return(tracker); }
/// <summary> /// Tries to load a <see cref="FileChangeTrackingSet"/> and resume tracking changes. /// If loading is successful (including matching the provided atomic save token), the returned tracker begins in the <see cref="FileChangeTrackingState.TrackingChanges"/> state. /// The caller may then query for new changes since the tracking set was last checkpointed and persisted, and may track additional files. /// Otherwise, a tracker with a new and empty change tracking set is created, initially in the <see cref="FileChangeTrackingState.BuildingInitialChangeTrackingSet"/> state. /// In that case, attempting to query changes will fail (the caller should fall back to doing complete rather than incremental work, thus populating the new change tracking set). /// </summary> /// <exception cref="BuildXLException">Thrown in the event of an I/O error other than the given path being absent.</exception> public static LoadingTrackerResult ResumeOrRestartTrackingChanges( LoggingContext loggingContext, VolumeMap volumeMap, IChangeJournalAccessor journal, FileChangeTrackerSupersedeMode supersedeMode, string path, string buildEngineFingerprint, out FileChangeTracker tracker) { Contract.Requires(volumeMap != null); Contract.Requires(journal != null); Contract.Requires(path != null); Contract.Ensures(Contract.Result <LoadingTrackerResult>() != null); Contract.Ensures(Contract.ValueAtReturn(out tracker) != null); Contract.Ensures( (Contract.Result <LoadingTrackerResult>().Succeeded&& Contract.ValueAtReturn(out tracker).IsTrackingChanges) || (!Contract.Result <LoadingTrackerResult>().Succeeded&& Contract.ValueAtReturn(out tracker).TrackingState == FileChangeTrackingState.BuildingInitialChangeTrackingSet)); using (var pm = BuildXL.Tracing.PerformanceMeasurement.StartWithoutStatistic( loggingContext, loggingContext1 => Logger.Log.StartLoadingChangeTracker(loggingContext1, path), loggingContext1 => Logger.Log.EndLoadingChangeTracker(loggingContext1))) { // Note that TryLoad may throw in the event of spooky I/O errors. var loadingTrackerResult = TryLoad(pm.LoggingContext, path, volumeMap, journal, supersedeMode, buildEngineFingerprint); if (loadingTrackerResult.Succeeded) { Contract.Assert(loadingTrackerResult.ChangeTrackingSet != null); // Ideally, we reload prior state so that the caller can query changes and do incremental work. // In this case, we already validated the correlating atomic save token, and so the state is safe to reuse. tracker = ResumeTrackingChanges( pm.LoggingContext, loadingTrackerResult.FileId, volumeMap, journal, loadingTrackerResult.ChangeTrackingSet, buildEngineFingerprint); } else { Contract.Assert(loadingTrackerResult.ChangeTrackingSet == null); // Or, we might be unable to re-use the persisted state. In that case we start over. Note that there's nothing to do with the correlating save token here; // on save, a new or existing token will be provided as appropriate. // The reason of the failure is already logged in the TryLoad() method above. tracker = StartTrackingChanges(pm.LoggingContext, volumeMap, journal, supersedeMode, buildEngineFingerprint); } Logger.Log.LoadingChangeTracker( pm.LoggingContext, path, loadingTrackerResult.FileId.ToString(), loadingTrackerResult.Status.ToString(), loadingTrackerResult.StatusAsString, loadingTrackerResult.TrackedVolumesCount, (long)loadingTrackerResult.TrackedJournalsSizeBytes, loadingTrackerResult.DurationMs); return(loadingTrackerResult); } }