private bool TryGetFingerprintStoreEntry(Process process, out FingerprintStoreEntry entry) { using (Counters.StartStopwatch(FingerprintStoreCounters.CacheMissFindOldEntriesTime)) { process.TryComputePipUniqueOutputHash(m_context.PathTable, out var pipUniqueOutputHash, m_logTarget.PipContentFingerprinter.PathExpander); return(PreviousFingerprintStore.TryGetFingerprintStoreEntry(pipUniqueOutputHash.ToString(), process.FormattedSemiStableHash, out entry)); } }
private void Analyze(FingerprintStoreEntry newEntry, Process pip, bool fromCacheLookup) { using (var watch = new CacheMissTimer(pip.PipId, this)) { if (!IsCacheMissEligible(pip.PipId)) { return; } TryGetFingerprintStoreEntry(pip, out FingerprintStoreEntry oldEntry); PerformCacheMissAnalysis(pip, oldEntry, newEntry, fromCacheLookup); } }
private void PerformCacheMissAnalysis(Process pip, FingerprintStoreEntry oldEntry, FingerprintStoreEntry newEntry, bool fromCacheLookup) { string pipDescription = pip.GetDescription(m_context); try { if (!m_pipCacheMissesDict.TryRemove(pip.PipId, out var missInfo)) { return; } MarkPipAsChanged(pip.PipId); if (Interlocked.Increment(ref m_numCacheMissPerformed) >= s_maxCacheMissCanPerform) { return; } if (fromCacheLookup) { Counters.IncrementCounter(FingerprintStoreCounters.CacheMissAnalysisAnalyzeCacheLookUpCount); } else { Counters.IncrementCounter(FingerprintStoreCounters.CacheMissAnalysisAnalyzeExecutionCount); } using (var pool = Pools.StringBuilderPool.GetInstance()) using (var writer = new StringWriter(pool.Instance)) using (Counters.StartStopwatch(FingerprintStoreCounters.CacheMissAnalysisAnalyzeDuration)) { CacheMissAnalysisUtilities.AnalyzeCacheMiss( writer, missInfo, () => new FingerprintStoreReader.PipRecordingSession(PreviousFingerprintStore, oldEntry), () => new FingerprintStoreReader.PipRecordingSession(m_logTarget.ExecutionFingerprintStore, newEntry), m_cacheMissDiffFormat); // The diff sometimes contains several empty new lines at the end. var reason = writer.ToString().TrimEnd(Environment.NewLine.ToCharArray()); pipDescription = pip.GetDescription(m_context); Logger.Log.CacheMissAnalysis(m_loggingContext, pipDescription, reason, fromCacheLookup); } } catch (Exception ex) { // Cache miss analysis shouldn't fail the build Logger.Log.CacheMissAnalysisException(m_loggingContext, pipDescription, ex.ToString(), oldEntry?.PipToFingerprintKeys.ToString(), newEntry?.PipToFingerprintKeys.ToString()); } }
/// <summary> /// Constructor /// </summary> public PipRecordingSession(FingerprintStore store, FingerprintStoreEntry entry, TextWriter textWriter = null) { m_store = store; m_entry = entry; PipWriter = textWriter; if (EntryExists && textWriter != null) { // Write all pip fingerprint information to a file, except for directory memberships. // Directory memberships are skipped unless there is a strong fingerprint miss // to avoid parsing the strong fingerprint entry. m_entry.Print(PipWriter); } }
internal void AnalyzeForExecution(FingerprintStoreEntry newEntry, Process pip) { using (var watch = new CacheMissTimer(pip.PipId, this)) { if (!IsCacheMissEligible(pip.PipId)) { return; } if (!TryGetFingerprintStoreEntry(pip, out FingerprintStoreEntry oldEntry)) { return; } PerformCacheMissAnalysis(pip, oldEntry, newEntry, false); } }
public void EnsureCacheLookupStoreIsFirst() { // Read only mount var sealDir = CreateUniqueDirectory(ReadonlyRoot); // Set up absent file under source seal directory var absentFile = CreateSourceFile(sealDir); File.Delete(ArtifactToString(absentFile)); var absentFileDir = CreateAndScheduleSealDirectoryArtifact(sealDir, SealDirectoryKind.SourceAllDirectories); // Create a pip that will have a strong fingerprint miss if absentFile is created var ops = new Operation[] { Operation.Probe(absentFile, doNotInfer: true), Operation.WriteFile(CreateOutputFileArtifact()) }; var builder = CreatePipBuilder(ops); builder.AddInputDirectory(absentFileDir); var pipA = SchedulePipBuilder(builder).Process; var srcB = CreateSourceFile(); var pipB = CreateAndSchedulePipBuilder(new Operation[] { Operation.ReadFile(srcB), Operation.WriteFile(CreateOutputFileArtifact()) }).Process; var build1 = RunScheduler().AssertCacheMiss(pipA.PipId); // Create /absentFile File.WriteAllText(ArtifactToString(absentFile), "asdf"); var build2 = RunScheduler().AssertCacheMiss(pipA.PipId); var correctOut1 = RunAnalyzer(build1, build2).AssertPipMiss( pipA, PipCacheMissType.MissForDescriptorsDueToStrongFingerprints, ArtifactToPrint(absentFile), ObservedInputConstants.AbsentPathProbe, ObservedInputConstants.FileContentRead).FileOutput; FingerprintStoreEntry pipAEntry = default; FingerprintStoreSession(ResultToStoreDirectory(build2), store => { // Remove pipA's entry from the execution-time fingerprintStore, and replace it with a dummy entry (pipB's) that will diff incorrectly if used store.TryGetFingerprintStoreEntryBySemiStableHash(pipA.FormattedSemiStableHash, out pipAEntry); store.RemoveFingerprintStoreEntryForTesting(pipAEntry); store.TryGetFingerprintStoreEntryBySemiStableHash(pipB.FormattedSemiStableHash, out var pipBEntry); // Route's pipA's key to point to the same fingerprint info as pipB's // If this store is used now to analyze pipA, the analysis will be incorrect pipBEntry.PipToFingerprintKeys = new System.Collections.Generic.KeyValuePair <string, PipFingerprintKeys>(pipA.FormattedSemiStableHash, pipBEntry.PipToFingerprintKeys.Value); store.PutFingerprintStoreEntry(pipBEntry); }, readOnly: false); // Make sure the diff is still correct, which implies the cache lookup fingerprint store is still being used var correctOut2 = RunAnalyzer(build1, build2).AssertPipMiss( pipA, PipCacheMissType.MissForDescriptorsDueToStrongFingerprints, ArtifactToPrint(absentFile), ObservedInputConstants.AbsentPathProbe, ObservedInputConstants.FileContentRead).FileOutput; XAssert.AreEqual(correctOut1, correctOut2); // Remove pipA's entry from the cache lookup store FingerprintStoreSession(ResultToStoreDirectory(build2, cacheLookupStore: true), store => { store.RemoveFingerprintStoreEntryForTesting(pipAEntry); }, readOnly: false); // Ensure that the analyzer falls-back on the execution-time store when the cache-lookup store is missing entries // srcB from pipB's dependencies will show up in the analysis because of earlier manipulation of the execution-time store var incorrectOut = RunAnalyzer(build1, build2).AssertAnalyzerPipMiss( pipA, PipCacheMissType.MissForDescriptorsDueToStrongFingerprints, ArtifactToPrint(srcB)).FileOutput; XAssert.AreNotEqual(correctOut2, incorrectOut); }
internal void AnalyzeForExecution(FingerprintStoreEntry newEntry, Process pip) { Analyze(newEntry, pip, fromCacheLookup: false); }
internal void AnalyzeForCacheLookup(FingerprintStoreEntry newEntry, Process pip) { Analyze(newEntry, pip, fromCacheLookup: true); }
private void PerformCacheMissAnalysis(Process pip, FingerprintStoreEntry oldEntry, FingerprintStoreEntry newEntry, bool fromCacheLookup) { Contract.Requires(pip != null); string pipDescription = pip.GetDescription(m_context); try { if (!m_pipCacheMissesDict.TryRemove(pip.PipId, out var missInfo)) { return; } MarkPipAsChanged(pip.PipId); if (fromCacheLookup) { Counters.IncrementCounter(FingerprintStoreCounters.CacheMissAnalysisAnalyzeCacheLookUpCount); } else { Counters.IncrementCounter(FingerprintStoreCounters.CacheMissAnalysisAnalyzeExecutionCount); } using (var pool = Pools.StringBuilderPool.GetInstance()) using (Counters.StartStopwatch(FingerprintStoreCounters.CacheMissAnalysisAnalyzeDuration)) { var resultAndDetail = CacheMissAnalysisUtilities.AnalyzeCacheMiss( missInfo, () => new FingerprintStoreReader.PipRecordingSession(PreviousFingerprintStore, oldEntry), () => new FingerprintStoreReader.PipRecordingSession(m_logTarget.ExecutionFingerprintStore, newEntry), m_cacheMissDiffFormat); pipDescription = pip.GetDescription(m_context); if (m_batchLoggingQueue != null) { m_batchLoggingQueue.Enqueue(resultAndDetail.Detail.ToJObjectWithPipInfo(pip.FormattedSemiStableHash, pipDescription, fromCacheLookup)); } else { var detail = new JObject( new JProperty(nameof(resultAndDetail.Detail.ActualMissType), resultAndDetail.Detail.ActualMissType), new JProperty(nameof(resultAndDetail.Detail.ReasonFromAnalysis), resultAndDetail.Detail.ReasonFromAnalysis), new JProperty(nameof(resultAndDetail.Detail.Info), resultAndDetail.Detail.Info)).ToString(); Logger.Log.CacheMissAnalysis(m_loggingContext, pipDescription, detail, fromCacheLookup); } m_testHooks?.AddCacheMiss( pip.PipId, new FingerprintStoreTestHooks.CacheMissData { DetailAndResult = resultAndDetail, IsFromCacheLookUp = fromCacheLookup }); } } catch (Exception ex) { // Cache miss analysis shouldn't fail the build Logger.Log.CacheMissAnalysisException(m_loggingContext, pipDescription, ex.ToString(), oldEntry?.PipToFingerprintKeys.ToString(), newEntry?.PipToFingerprintKeys.ToString()); } }