private void NotifyCollection(TraceGC gc) { var listeners = GarbageCollection; if (listeners == null) { return; } var sizesBefore = GetBeforeGenerationSizes(gc); var sizesAfter = GetAfterGenerationSizes(gc); listeners?.Invoke(this, new GarbageCollectionArgs( _processId, gc.StartRelativeMSec, gc.Number, gc.Generation, (GarbageCollectionReason)gc.Reason, (GarbageCollectionType)gc.Type, !gc.IsNotCompacting(), gc.HeapStats.GenerationSize0, gc.HeapStats.GenerationSize1, gc.HeapStats.GenerationSize2, gc.HeapStats.GenerationSize3, sizesBefore, sizesAfter, gc.SuspendDurationMSec, gc.PauseDurationMSec, gc.BGCFinalPauseMSec )); }
public void Process(int processId, TraceGC gc) { var args = mapper.Map(processId, gc); foreach (var handler in handlers) { handler.Handle(args); } }
private static void WriteFormattedTraceGC(TextWriter tw, string prefix, TraceGC gc) { // Fields are not really obsolete, but experimental. #pragma warning disable CS0618 double mbBefore = gc.HeapSizeBeforeMB; double mbAfter = gc.HeapSizeAfterMB; #pragma warning restore CS0618 tw.WriteLine("{0} heap size before {1:N2} MB, after {2:N2} MB, {3:N2} % freed.", prefix, mbBefore, mbAfter, (100 - (((double)mbAfter / mbBefore) * 100))); }
private void RuntimeGCEnd(TraceProcess traceProcess, TraceGC gc) { if (traceProcess.ProcessID == _currentProcessId) { if (!_isMetricAlreadyCaptured) { lock (_lock) _isMetricAlreadyCaptured = true; } _gcTimeInTicks = (long)gc.DurationMSec * 10_000; _gcCount = (uint)gc.Number; } }
private static long[] GetGenerationSizes(TraceGC gc, bool before) { var sizes = new long[4]; if (gc.PerHeapHistories == null) { return(sizes); } foreach (var history in gc.PerHeapHistories) { for (var gen = 0; gen <= 3; gen++) { sizes[gen] += before ? history.GenData[gen].ObjSpaceBefore : history.GenData[gen].ObjSizeAfter; } } return(sizes); }
private int FindEvent(double tick, int start) { for (int i = start; i < m_heapInfo.GcEvents.Count; i++) { TraceGC gc = m_heapInfo.GcEvents[i]; if (tick >= gc.PauseStartRelativeMSec) { if (tick < gc.PauseStartRelativeMSec + gc.PauseDurationMSec) { return(i); } } else { break; } } return(-1); }
private long[] GetGenerationSizes(TraceGC gc, bool before) { var sizes = new long[4]; if (gc.PerHeapHistories == null) { return(sizes); } for (int heap = 0; heap < gc.PerHeapHistories.Count; heap++) { // LOH = 3 for (int gen = 0; gen <= 3; gen++) { sizes[gen] += before ? gc.PerHeapHistories[heap].GenData[gen].ObjSpaceBefore: gc.PerHeapHistories[heap].GenData[gen].ObjSizeAfter; } } return(sizes); }
private void OnOpenInducedStacks(object sender, RoutedEventArgs e) { StackSourceBuilder builder = new StackSourceBuilder(m_traceLog); List <TraceGC> events = m_runtime.GC.GCs; for (int i = 0; i < events.Count; i++) { TraceGC ev = events[i]; if (ev.IsInduced()) { GcEventExtra extra = GetGcEventExtra(ev.Number, false); if (extra != null) { builder.AddSample(extra.GCStartThread, ev.PauseDurationMSec, ev.StartRelativeMSec, String.Format("StartGC({0}, {1}, G{2})", ev.Reason, ev.Type, ev.Generation), extra.GCStartIndex); } } } StackSource source = builder.Stacks; if (source.SampleIndexLimit == 0) { MessageBox.Show("No stacks found for induced GC", ".Net Heap Analyzer", MessageBoxButton.OK); } else { StackWindow stackWindow = null; m_dataFile.StackWindowTo(null, ref stackWindow, source, "Induced GC", FirstEventTime, LastEventTime); } }
public GarbageCollectionArgs Map(int processId, TraceGC gc) { var sizesBefore = GetGenerationSizes(gc, true); var sizesAfter = GetGenerationSizes(gc, false); return(new GarbageCollectionArgs( processId, gc.StartRelativeMSec, gc.Number, gc.Generation, (GarbageCollectionReason)gc.Reason, (GarbageCollectionType)gc.Type, !gc.IsNotCompacting(), gc.HeapStats.GenerationSize0, gc.HeapStats.GenerationSize1, gc.HeapStats.GenerationSize2, gc.HeapStats.GenerationSize3, sizesBefore, sizesAfter, gc.SuspendDurationMSec, gc.PauseDurationMSec, gc.BGCFinalPauseMSec )); }
public List <Issue> GetIssues() { m_issues = new List <Issue>(); if (m_runtime.GC.Stats().TotalAllocatedMB == 0) { AddIssue(IssueType.Profiling, "No .Net heap allocation found.", "Turn on Clr/ClrPrivate ETW event providers and profile again."); } if (m_allocSites.Count == 0) { AddIssue(IssueType.Profiling, "No .Net allocation tick event found.", "Turn on Clr allocation tick event and profile again."); } if (m_process.CPUMSec == 0) { AddIssue(IssueType.Profiling, "No CPU sample event found.", "Turn on CPU sample event and profile again."); } else { double gcCpu = m_runtime.GC.Stats().TotalCpuMSec * 100.0 / m_process.CPUMSec; if (gcCpu >= 40) { AddIssue(IssueType.Cpu, String.Format("GC CPU usage extremely high ({0:N1} %)", gcCpu), "Check memory allocation, fragmentation, data structure, object refereence"); } else if (gcCpu >= 10) { AddIssue(IssueType.Cpu, String.Format("GC CPU usage higher than normal ({0:N1} %)", gcCpu), "Check memory allocation, fragmentation, data structure, object refereence"); } } List <TraceGC> events = m_runtime.GC.GCs; for (int i = 0; i < events.Count; i++) { TraceGC e = events[i]; if (e.IsInduced()) { m_induced++; m_inducedPause += e.PauseDurationMSec; } if (e.IsAllocLarge()) { m_allocLarge++; m_allocLargePause += e.PauseDurationMSec; } } if (m_induced != 0) { AddIssue( IssueType.Cpu, String.Format("There are {0:N0} induced GCs, causing total {1:N3} ms pause", m_induced, m_inducedPause), "Check call stack to figure out who is inducing GC"); m_issue.Action = "Induced GC Stacks"; m_issue.OnClick = OnOpenInducedStacks; } if (m_allocLarge != 0) { AddIssue( IssueType.Cpu, String.Format("There are {0:N0} large object GCs, causing total {1:N3} ms pause", m_allocLarge, m_allocLargePause), "Check call stacks to find LOH allocations"); m_issue.Action = "LOH Allocation Stacks"; m_issue.OnClick = OnOpenLargeAllocStacks; } return(m_issues); }
internal GcEventWrapper(TraceGC evnt) { m_event = evnt; }
/// <summary> /// Filter events according to checkboxes /// </summary> IEnumerable <GcEventWrapper> FilterEvent() { bool no_g0 = m_g0.UnChecked(); bool no_g1 = m_g1.UnChecked(); bool no_g2Blk = m_g2Blocking.UnChecked(); bool no_g2Bak = m_g2Background.UnChecked(); bool no_inducedBlocking = m_inducedBlocking.UnChecked(); bool no_inducedNonBlock = m_inducedNonblocking.UnChecked(); bool no_lowMemory = m_lowMemory.UnChecked(); bool no_allocSOH = m_allocSOH.UnChecked(); bool no_allocLOH = m_AllocLOH.UnChecked(); foreach (GcEventWrapper er in m_gcEvents) { TraceGC e = er.Event; GCType typ = e.Type; switch (e.Generation) { case 0: if (no_g0) { continue; } break; case 1: if (no_g1) { continue; } break; case 2: if (no_g2Blk && (typ == GCType.NonConcurrentGC)) { continue; } if (no_g2Bak && (typ == GCType.BackgroundGC)) { continue; } break; } switch (e.Reason) { case GCReason.OutOfSpaceSOH: case GCReason.AllocSmall: if (no_allocSOH) { continue; } break; case GCReason.InducedNotForced: case GCReason.Induced: if (no_inducedBlocking && (typ == GCType.NonConcurrentGC)) { continue; } if (no_inducedNonBlock && (typ == GCType.BackgroundGC)) { continue; } break; case GCReason.OutOfSpaceLOH: case GCReason.AllocLarge: if (no_allocLOH) { continue; } break; case GCReason.LowMemory: case GCReason.InducedLowMemory: if (no_lowMemory) { continue; } break; case GCReason.Empty: case GCReason.Internal: default: break; } yield return(er); } }
private static void RuntimeOnGCEnd(TraceProcess p, TraceGC gc) { Console.WriteLine($"{gc.GCGenerationName} {gc.Reason} {gc.PauseDurationMSec:F2}/{gc.DurationMSec:F2}"); }
private long[] GetAfterGenerationSizes(TraceGC gc) { var after = false; return(GetGenerationSizes(gc, after)); }
private long[] GetBeforeGenerationSizes(TraceGC gc) { var before = true; return(GetGenerationSizes(gc, before)); }
private static void RuntimeOnGCEnd(TraceProcess tp, TraceGC gc) { Console.WriteLine($"[{DateTime.Now.ToLongTimeString()}] GLAD received from {tp.Name}/{tp.ProcessID}"); Console.WriteLine($" {gc.GCGenerationName} {gc.Reason} Pause: {gc.PauseDurationMSec:F2} ms/{gc.DurationMSec:F2} ms"); Console.WriteLine(); }