示例#1
0
        private RuntimeCacheMissAnalyzer(
            FingerprintStoreExecutionLogTarget logTarget,
            LoggingContext loggingContext,
            PipExecutionContext context,
            FingerprintStore previousFingerprintStore,
            IReadonlyDirectedGraph graph,
            IDictionary <PipId, RunnablePipPerformanceInfo> runnablePipPerformance,
            CacheMissDiffFormat cacheMissDiffFormat,
            bool cacheMissBatch,
            FingerprintStoreTestHooks testHooks = null)
        {
            m_loggingContext         = loggingContext;
            m_logTarget              = logTarget;
            m_context                = context;
            PreviousFingerprintStore = previousFingerprintStore;
            m_visitor                = new NodeVisitor(graph);
            m_changedPips            = new VisitationTracker(graph);
            m_pipCacheMissesDict     = new ConcurrentDictionary <PipId, PipCacheMissInfo>();
            m_runnablePipPerformance = runnablePipPerformance;
            m_cacheMissDiffFormat    = cacheMissDiffFormat;
            m_maxCacheMissCanPerform = cacheMissBatch ? EngineEnvironmentSettings.MaxNumPipsForCacheMissAnalysis.Value * EngineEnvironmentSettings.MaxMessagesPerBatch : EngineEnvironmentSettings.MaxNumPipsForCacheMissAnalysis.Value;

            m_batchLoggingQueue = cacheMissBatch ? NagleQueue <JProperty> .Create(
                BatchLogging,
                maxDegreeOfParallelism : 1,
                interval : TimeSpan.FromMinutes(1),
                batchSize : EngineEnvironmentSettings.MaxMessagesPerBatch) : null;


            m_testHooks = testHooks;
            m_testHooks?.InitRuntimeCacheMisses();
        }
示例#2
0
        /// <summary>
        /// Analyzes the cache miss for a specific pip.
        /// </summary>
        public static CacheMissAnalysisDetailAndResult AnalyzeCacheMiss(
            PipCacheMissInfo missInfo,
            Func <PipRecordingSession> oldSessionFunc,
            Func <PipRecordingSession> newSessionFunc,
            CacheMissDiffFormat diffFormat)
        {
            Contract.Requires(oldSessionFunc != null);
            Contract.Requires(newSessionFunc != null);

            var cacheMissType = missInfo.CacheMissType.ToString();
            var cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType);

            switch (missInfo.CacheMissType)
            {
            // Fingerprint miss.
            case PipCacheMissType.MissForDescriptorsDueToAugmentedWeakFingerprints:
            case PipCacheMissType.MissForDescriptorsDueToWeakFingerprints:
            case PipCacheMissType.MissForDescriptorsDueToStrongFingerprints:
                // Compute the pip unique output hash to use as the primary lookup key for fingerprint store entries
                cacheMissAnalysisDetailAndResult = AnalyzeFingerprints(oldSessionFunc, newSessionFunc, diffFormat, cacheMissType);
                break;

            // We had a weak and strong fingerprint match, but couldn't retrieve correct data from the cache
            case PipCacheMissType.MissForCacheEntry:
                cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType, CacheMissAnalysisResult.DataMiss, "Cache entry missing from the cache.");
                break;

            case PipCacheMissType.MissForProcessMetadata:
            case PipCacheMissType.MissForProcessMetadataFromHistoricMetadata:
                cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType, CacheMissAnalysisResult.DataMiss, "MetaData missing from the cache.");
                break;

            case PipCacheMissType.MissForProcessOutputContent:
                cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType, CacheMissAnalysisResult.OutputMiss, "Outputs missing from the cache.", new JObject(new JProperty("MissingOutputs", missInfo.MissedOutputs)));
                break;

            case PipCacheMissType.MissDueToInvalidDescriptors:
                cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType, CacheMissAnalysisResult.InvalidDescriptors, "Cache returned invalid data.");
                break;

            case PipCacheMissType.MissForDescriptorsDueToArtificialMissOptions:
                cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType, CacheMissAnalysisResult.ArtificialMiss, "Cache miss artificially forced by user.");
                break;

            case PipCacheMissType.Hit:
                cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType, CacheMissAnalysisResult.NoMiss, "Pip was a cache hit.");
                break;

            case PipCacheMissType.Invalid:
                cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType, CacheMissAnalysisResult.Invalid, "No valid changes or cache issues were detected to cause process execution, but a process still executed.");
                break;

            default:
                break;
            }

            return(cacheMissAnalysisDetailAndResult);
        }
示例#3
0
        /// <summary>
        /// Analyzes the cache miss for a specific pip.
        /// </summary>
        public static CacheMissAnalysisResult AnalyzeCacheMiss(
            TextWriter writer,
            PipCacheMissInfo missInfo,
            Func <PipRecordingSession> oldSessionFunc,
            Func <PipRecordingSession> newSessionFunc,
            CacheMissDiffFormat diffFormat)
        {
            Contract.Requires(oldSessionFunc != null);
            Contract.Requires(newSessionFunc != null);

            WriteLine($"Cache miss type: {missInfo.CacheMissType}", writer);
            WriteLine(string.Empty, writer);

            switch (missInfo.CacheMissType)
            {
            // Fingerprint miss
            case PipCacheMissType.MissForDescriptorsDueToWeakFingerprints:
            case PipCacheMissType.MissForDescriptorsDueToStrongFingerprints:
                // Compute the pip unique output hash to use as the primary lookup key for fingerprint store entries
                return(AnalyzeFingerprints(oldSessionFunc, newSessionFunc, writer, diffFormat));

            // We had a weak and strong fingerprint match, but couldn't retrieve correct data from the cache
            case PipCacheMissType.MissForCacheEntry:
                WriteLine($"Cache entry missing from the cache.", writer);
                return(CacheMissAnalysisResult.DataMiss);

            case PipCacheMissType.MissForProcessMetadata:
            case PipCacheMissType.MissForProcessMetadataFromHistoricMetadata:
                WriteLine($"MetaData missing from the cache.", writer);
                return(CacheMissAnalysisResult.DataMiss);

            case PipCacheMissType.MissForProcessOutputContent:
                WriteLine(new JProperty("MissingOutputs", missInfo.MissedOutputs).ToString(), writer);
                return(CacheMissAnalysisResult.OutputMiss);

            case PipCacheMissType.MissDueToInvalidDescriptors:
                WriteLine($"Cache returned invalid data.", writer);
                return(CacheMissAnalysisResult.InvalidDescriptors);

            case PipCacheMissType.MissForDescriptorsDueToArtificialMissOptions:
                WriteLine($"Cache miss artificially forced by user.", writer);
                return(CacheMissAnalysisResult.ArtificialMiss);

            case PipCacheMissType.Invalid:
                WriteLine($"Unexpected condition! No valid changes or cache issues were detected to cause process execution, but a process still executed.", writer);
                return(CacheMissAnalysisResult.Invalid);

            case PipCacheMissType.Hit:
                WriteLine($"Pip was a cache hit.", writer);
                return(CacheMissAnalysisResult.NoMiss);

            default:
                WriteLine($"Unexpected condition! Unknown cache miss type.", writer);
                return(CacheMissAnalysisResult.Invalid);
            }
        }
示例#4
0
        public Analyzer InitializeFingerprintStoreAnalyzer(AnalysisInput oldAnalysisInput, AnalysisInput newAnalysisInput)
        {
            string outputDirectory = null;
            bool   allPips         = false;
            bool   noBanner        = false;
            long   sshValue        = -1;
            CacheMissDiffFormat cacheMissDiffFormat = CacheMissDiffFormat.CustomJsonDiff;

            foreach (var opt in AnalyzerOptions)
            {
                if (opt.Name.Equals("outputDirectory", StringComparison.OrdinalIgnoreCase) ||
                    opt.Name.Equals("o", StringComparison.OrdinalIgnoreCase))
                {
                    outputDirectory = ParseSingletonPathOption(opt, outputDirectory);
                }
                else if (opt.Name.Equals("pip", StringComparison.OrdinalIgnoreCase) ||
                         opt.Name.Equals("p", StringComparison.OrdinalIgnoreCase))
                {
                    sshValue = ParseSemistableHash(opt);
                }
                else if (opt.Name.StartsWith("allPips", StringComparison.OrdinalIgnoreCase))
                {
                    allPips = ParseBooleanOption(opt);
                }
                else if (opt.Name.Equals("nobanner", StringComparison.OrdinalIgnoreCase))
                {
                    noBanner = ParseBooleanOption(opt);
                }
                else if (opt.Name.Equals("cacheMissDiffFormat", StringComparison.OrdinalIgnoreCase))
                {
                    cacheMissDiffFormat = ParseEnumOption <CacheMissDiffFormat>(opt);
                }
                else
                {
                    throw Error("Unknown option for cache miss analysis: {0}", opt.Name);
                }
            }

            if (string.IsNullOrEmpty(outputDirectory))
            {
                throw new Exception("'outputDirectory' is required.");
            }

            if (allPips && sshValue != -1)
            {
                throw new Exception("'allPips' can't be true if pipId is set.");
            }

            return(new FingerprintStoreAnalyzer(oldAnalysisInput, newAnalysisInput)
            {
                OutputDirectory = outputDirectory,
                AllPips = allPips,
                SemiStableHashToRun = sshValue,
                NoBanner = noBanner
            });
        }
示例#5
0
 private RuntimeCacheMissAnalyzer(
     FingerprintStoreExecutionLogTarget logTarget,
     LoggingContext loggingContext,
     PipExecutionContext context,
     FingerprintStore previousFingerprintStore,
     IReadonlyDirectedGraph graph,
     IDictionary <PipId, RunnablePipPerformanceInfo> runnablePipPerformance,
     CacheMissDiffFormat cacheMissDiffFormat)
 {
     m_loggingContext         = loggingContext;
     m_logTarget              = logTarget;
     m_context                = context;
     PreviousFingerprintStore = previousFingerprintStore;
     m_visitor                = new NodeVisitor(graph);
     m_changedPips            = new VisitationTracker(graph);
     m_pipCacheMissesDict     = new ConcurrentDictionary <PipId, PipCacheMissInfo>();
     m_runnablePipPerformance = runnablePipPerformance;
     m_cacheMissDiffFormat    = cacheMissDiffFormat;
 }
示例#6
0
        private static CacheMissAnalysisResult AnalyzeFingerprints(
            Func <PipRecordingSession> oldSessionFunc,
            Func <PipRecordingSession> newSessionFunc,
            TextWriter writer,
            CacheMissDiffFormat diffFormat)
        {
            var result = CacheMissAnalysisResult.Invalid;

            // While a PipRecordingSession is in scope, any pip information retrieved from the fingerprint store is
            // automatically written out to per-pip files.
            using (var oldPipSession = oldSessionFunc())
                using (var newPipSession = newSessionFunc())
                {
                    bool missingPipEntry = false;
                    if (!oldPipSession.EntryExists)
                    {
                        WriteLine("No fingerprint computation data found from old build.", writer, oldPipSession.PipWriter);
                        WriteLine("This may be the first execution where pip outputs were stored to the cache.", writer, oldPipSession.PipWriter);

                        // Write to just the old pip file
                        WriteLine(RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching, oldPipSession.PipWriter);
                        missingPipEntry = true;
                        result          = CacheMissAnalysisResult.MissingFromOldBuild;

                        WriteLine(string.Empty, writer, oldPipSession.PipWriter);
                    }

                    if (!newPipSession.EntryExists)
                    {
                        // Cases:
                        // 1. ScheduleProcessNotStoredToCacheDueToFileMonitoringViolations
                        // 2. ScheduleProcessNotStoredDueToMissingOutputs
                        // 3. ScheduleProcessNotStoredToWarningsUnderWarnAsError
                        // 4. ScheduleProcessNotStoredToCacheDueToInherentUncacheability
                        WriteLine("No fingerprint computation data found from new build.", writer, newPipSession.PipWriter);

                        // Write to just the new pip file
                        WriteLine(RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching, newPipSession.PipWriter);
                        missingPipEntry = true;
                        result          = CacheMissAnalysisResult.MissingFromNewBuild;

                        WriteLine(string.Empty, writer, newPipSession.PipWriter);
                    }

                    if (missingPipEntry)
                    {
                        // Only write once to the analysis file
                        WriteLine(RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching, writer);

                        // Nothing to compare if an entry is missing
                        return(result);
                    }

                    if (oldPipSession.FormattedSemiStableHash != newPipSession.FormattedSemiStableHash)
                    {
                        // Make trivial json so the print looks like the rest of the diff
                        if (diffFormat == CacheMissDiffFormat.CustomJsonDiff)
                        {
                            var diff = new JProperty("SemiStableHash",
                                                     new JObject(
                                                         new JProperty("Old", oldPipSession.FormattedSemiStableHash),
                                                         new JProperty("New", newPipSession.FormattedSemiStableHash)));
                            WriteLine(new JObject(diff).ToString(), writer);
                        }
                        else
                        {
                            var oldNode = new JsonNode
                            {
                                Name = RepeatedStrings.FormattedSemiStableHashChanged
                            };
                            oldNode.Values.Add(oldPipSession.FormattedSemiStableHash);

                            var newNode = new JsonNode
                            {
                                Name = RepeatedStrings.FormattedSemiStableHashChanged
                            };
                            newNode.Values.Add(newPipSession.FormattedSemiStableHash);

                            WriteLine(JsonTree.PrintTreeDiff(oldNode, newNode), writer);
                        }
                    }

                    // Diff based off the actual fingerprints instead of the PipCacheMissType
                    // to avoid shared cache diff confusion.
                    //
                    // In the following shared cache scenario:
                    // Local cache: WeakFingerprint miss
                    // Remote cache: WeakFingerprint hit, StrongFingerprint miss
                    //
                    // The pip cache miss type will be a strong fingerprint miss,
                    // but the data in the fingerprint store will not match the
                    // remote cache's, so we diff based off what we have in the fingerprint store.
                    if (oldPipSession.WeakFingerprint != newPipSession.WeakFingerprint)
                    {
                        WriteLine("WeakFingerprint", writer);

                        if (diffFormat == CacheMissDiffFormat.CustomJsonDiff)
                        {
                            WriteLine(oldPipSession.DiffWeakFingerprint(newPipSession).ToString(), writer);
                        }
                        else
                        {
                            WriteLine(JsonTree.PrintTreeDiff(oldPipSession.GetWeakFingerprintTree(), newPipSession.GetWeakFingerprintTree()), writer);
                        }

                        result = CacheMissAnalysisResult.WeakFingerprintMismatch;
                    }
                    else if (oldPipSession.PathSetHash != newPipSession.PathSetHash)
                    {
                        WriteLine($"PathSet", writer);

                        if (diffFormat == CacheMissDiffFormat.CustomJsonDiff)
                        {
                            WriteLine(oldPipSession.DiffPathSet(newPipSession).ToString(), writer);
                        }
                        else
                        {
                            // JsonPatchDiff does not have pathset comparison.
                            WriteLine(JsonTree.PrintTreeDiff(oldPipSession.GetStrongFingerprintTree(), newPipSession.GetStrongFingerprintTree()), writer);
                        }

                        result = CacheMissAnalysisResult.PathSetHashMismatch;
                    }
                    else if (oldPipSession.StrongFingerprint != newPipSession.StrongFingerprint)
                    {
                        WriteLine("StrongFingerprint", writer);

                        if (diffFormat == CacheMissDiffFormat.CustomJsonDiff)
                        {
                            WriteLine(oldPipSession.DiffStrongFingerprint(newPipSession).ToString(), writer);
                        }
                        else
                        {
                            WriteLine(JsonTree.PrintTreeDiff(oldPipSession.GetStrongFingerprintTree(), newPipSession.GetStrongFingerprintTree()), writer);
                        }

                        result = CacheMissAnalysisResult.StrongFingerprintMismatch;
                    }
                    else
                    {
                        WriteLine("The fingerprints from both builds matched and no cache retrieval errors occurred.", writer);
                        WriteLine(RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching, writer, oldPipSession.PipWriter, newPipSession.PipWriter);
                        result = CacheMissAnalysisResult.UncacheablePip;
                    }
                }

            return(result);
        }
示例#7
0
        private static CacheMissAnalysisDetailAndResult AnalyzeFingerprints(
            Func <PipRecordingSession> oldSessionFunc,
            Func <PipRecordingSession> newSessionFunc,
            CacheMissDiffFormat diffFormat,
            string cacheMissType)
        {
            var cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType);

            // While a PipRecordingSession is in scope, any pip information retrieved from the fingerprint store is
            // automatically written out to per-pip files.
            using (var oldPipSession = oldSessionFunc())
                using (var newPipSession = newSessionFunc())
                {
                    if (!oldPipSession.EntryExists)
                    {
                        cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType, CacheMissAnalysisResult.MissingFromOldBuild, $"No fingerprint computation data found from old build. This may be the first execution where pip outputs were stored to the cache. {RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching}");

                        // Write to just the old pip file
                        WriteLine(RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching, oldPipSession.PipWriter);
                        WriteLine(string.Empty, oldPipSession.PipWriter);

                        return(cacheMissAnalysisDetailAndResult);
                    }

                    if (!newPipSession.EntryExists)
                    {
                        // Cases:
                        // 1. ScheduleProcessNotStoredToCacheDueToFileMonitoringViolations
                        // 2. ScheduleProcessNotStoredDueToMissingOutputs
                        // 3. ScheduleProcessNotStoredToWarningsUnderWarnAsError
                        // 4. ScheduleProcessNotStoredToCacheDueToInherentUncacheability
                        cacheMissAnalysisDetailAndResult = new CacheMissAnalysisDetailAndResult(cacheMissType, CacheMissAnalysisResult.MissingFromNewBuild, $"No fingerprint computation data found from new build. {RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching}");
                        // Write to just the new pip file
                        WriteLine(RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching, newPipSession.PipWriter);
                        WriteLine(string.Empty, newPipSession.PipWriter);

                        return(cacheMissAnalysisDetailAndResult);
                    }

                    if (oldPipSession.FormattedSemiStableHash != newPipSession.FormattedSemiStableHash)
                    {
                        cacheMissAnalysisDetailAndResult.Detail.ReasonFromAnalysis = "SemiStableHashs of the builds are different.";
                        // Make trivial json so the print looks like the rest of the diff
                        if (diffFormat == CacheMissDiffFormat.CustomJsonDiff)
                        {
                            cacheMissAnalysisDetailAndResult.Detail.AddPropertyToCacheMissInfo("SemiStableHash",
                                                                                               new JObject(
                                                                                                   new JProperty("Old", oldPipSession.FormattedSemiStableHash),
                                                                                                   new JProperty("New", newPipSession.FormattedSemiStableHash)));
                        }
                        else
                        {
                            var oldNode = new JsonNode
                            {
                                Name = RepeatedStrings.FormattedSemiStableHashChanged
                            };
                            oldNode.Values.Add(oldPipSession.FormattedSemiStableHash);

                            var newNode = new JsonNode
                            {
                                Name = RepeatedStrings.FormattedSemiStableHashChanged
                            };
                            newNode.Values.Add(newPipSession.FormattedSemiStableHash);
                            cacheMissAnalysisDetailAndResult.Detail.AddPropertyToCacheMissInfo("SemiStableHash", JsonTree.PrintTreeDiff(oldNode, newNode));
                        }
                    }

                    // Diff based off the actual fingerprints instead of the PipCacheMissType
                    // The actual miss type can mismatch the reason from analysis, since cache miss analyzer is a best effort approach.
                    // There are several circumstances that fingerprintstore doesn't not match the real cache for pip's cache lookup.
                    // 1. Fingerprint store used for cache miss analysis is load at the beginning of the build and store at the end of the build,
                    //    while cache entries in cache is store and retrieve during the build. There is a chance that the entry of a pip retrieved
                    //    in cache look up time is not the same entry in the fingerprint store for analysis.
                    //    Example: PipA executes in three builds in sequence Build1, Build2, Build3. Build1 finished before Build2 and Build3 starts.
                    //             So, the fingerprint store created/updated in Build1 get stored in cache.
                    //             Then Build2 and Build 3 starts and load the fingerprint store for cache miss analysis.
                    //             PipA in Build2 finished execution and get stored in cache.
                    //             PipB in Build3 started to execute. It get cache miss.
                    //             Its actual cache miss type is from the cache look up result. In this example is the result compare to PipA in Build2.
                    //             However, the cache miss analysis result is a comparison between PipA in Build1 and PipA in Build3.
                    //             So, the ActualMissType can mismatch ReasonFromAnalysis.
                    // 2. Not all builds contribute to cache enable cache miss analysis, so the fingerprint store doesn't not have a full collection of entry from all builds.

                    if (oldPipSession.WeakFingerprint != newPipSession.WeakFingerprint)
                    {
                        cacheMissAnalysisDetailAndResult.Detail.ReasonFromAnalysis = "WeakFingerprints of the builds are different.";
                        if (diffFormat == CacheMissDiffFormat.CustomJsonDiff)
                        {
                            cacheMissAnalysisDetailAndResult.Detail.AddPropertyToCacheMissInfo("WeakFingerprintMismatchResult", oldPipSession.DiffWeakFingerprint(newPipSession));
                        }
                        else
                        {
                            cacheMissAnalysisDetailAndResult.Detail.AddPropertyToCacheMissInfo("WeakFingerprintMismatchResult", PrintTreeDiff(oldPipSession.GetWeakFingerprintTree(), newPipSession.GetWeakFingerprintTree(), oldPipSession));
                        }

                        cacheMissAnalysisDetailAndResult.Result = CacheMissAnalysisResult.WeakFingerprintMismatch;
                    }
                    else if (oldPipSession.PathSetHash != newPipSession.PathSetHash)
                    {
                        cacheMissAnalysisDetailAndResult.Detail.ReasonFromAnalysis = "PathSets of the builds are different.";
                        if (diffFormat == CacheMissDiffFormat.CustomJsonDiff)
                        {
                            cacheMissAnalysisDetailAndResult.Detail.AddPropertyToCacheMissInfo("PathSetMismatchResult", oldPipSession.DiffPathSet(newPipSession));
                        }
                        else
                        {
                            cacheMissAnalysisDetailAndResult.Detail.AddPropertyToCacheMissInfo("PathSetMismatchResult", PrintTreeDiff(oldPipSession.GetStrongFingerprintTree(), newPipSession.GetStrongFingerprintTree(), oldPipSession));
                        }

                        cacheMissAnalysisDetailAndResult.Result = CacheMissAnalysisResult.PathSetHashMismatch;
                    }
                    else if (oldPipSession.StrongFingerprint != newPipSession.StrongFingerprint)
                    {
                        cacheMissAnalysisDetailAndResult.Detail.ReasonFromAnalysis = "StrongFingerprints of the builds are different.";
                        if (diffFormat == CacheMissDiffFormat.CustomJsonDiff)
                        {
                            cacheMissAnalysisDetailAndResult.Detail.AddPropertyToCacheMissInfo("StrongFingerprintMismatchResult", oldPipSession.DiffStrongFingerprint(newPipSession));
                        }
                        else
                        {
                            cacheMissAnalysisDetailAndResult.Detail.AddPropertyToCacheMissInfo("StrongFingerprintMismatchResult", PrintTreeDiff(oldPipSession.GetStrongFingerprintTree(), newPipSession.GetStrongFingerprintTree(), oldPipSession));
                        }

                        cacheMissAnalysisDetailAndResult.Result = CacheMissAnalysisResult.StrongFingerprintMismatch;
                    }
                    else
                    {
                        cacheMissAnalysisDetailAndResult.Detail.ReasonFromAnalysis = $"The fingerprints from both builds matched and no cache retrieval errors occurred. {RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching}";
                        WriteLine(RepeatedStrings.DisallowedFileAccessesOrPipFailuresPreventCaching, oldPipSession.PipWriter, newPipSession.PipWriter);
                        cacheMissAnalysisDetailAndResult.Result = CacheMissAnalysisResult.UncacheablePip;
                    }
                }

            return(cacheMissAnalysisDetailAndResult);
        }