diff --git a/src/EtwHeapDump/DotNetHeapDumpGraphReader.cs b/src/EtwHeapDump/DotNetHeapDumpGraphReader.cs index 367f21220..be291f223 100644 --- a/src/EtwHeapDump/DotNetHeapDumpGraphReader.cs +++ b/src/EtwHeapDump/DotNetHeapDumpGraphReader.cs @@ -182,6 +182,12 @@ internal void SetupCallbacks(MemoryGraph memoryGraph, TraceEventDispatcher sourc } }; + source.Clr.GCGenAwareStart += delegate (GenAwareBeginTraceData data) + { + m_seenStart = true; + m_ignoreEvents = false; + }; + source.Clr.GCStart += delegate (GCStartTraceData data) { // If this GC is not part of a heap dump, ignore it. @@ -231,8 +237,6 @@ internal void SetupCallbacks(MemoryGraph memoryGraph, TraceEventDispatcher sourc } }; - - source.Clr.GCStop += delegate (GCEndTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) @@ -262,6 +266,17 @@ internal void SetupCallbacks(MemoryGraph memoryGraph, TraceEventDispatcher sourc } }; + source.Clr.GCGenAwareEnd += delegate (GenAwareEndTraceData data) + { + m_ignoreEvents = true; + if (m_nodeBlocks.Count == 0 && m_typeBlocks.Count == 0 && m_edgeBlocks.Count == 0) + { + m_log.WriteLine("Found no node events, looking for another GC"); + m_seenStart = false; + return; + } + }; + source.Clr.TypeBulkType += delegate (GCBulkTypeTraceData data) { // Don't check m_ignoreEvents here, as BulkType events can be emitted by other events...such as the GC allocation event. @@ -474,6 +489,9 @@ internal void SetupCallbacks(MemoryGraph memoryGraph, TraceEventDispatcher sourc case 3: segment.Gen3End = end; break; + case 4: + segment.Gen4End = end; + break; default: throw new Exception("Invalid generation in GCGenerationRangeTraceData"); } diff --git a/src/HeapDumpCommon/DotNetHeapInfo.cs b/src/HeapDumpCommon/DotNetHeapInfo.cs index 27f01b9c1..7a79bce91 100644 --- a/src/HeapDumpCommon/DotNetHeapInfo.cs +++ b/src/HeapDumpCommon/DotNetHeapInfo.cs @@ -115,6 +115,7 @@ public class GCHeapDumpSegment : IFastSerializable public Address Gen1End { get; internal set; } public Address Gen2End { get; internal set; } public Address Gen3End { get; internal set; } + public Address Gen4End { get; internal set; } #region private void IFastSerializable.ToStream(Serializer serializer) diff --git a/src/PerfView/PerfViewData.cs b/src/PerfView/PerfViewData.cs index 4a47f5251..1a6971b3d 100644 --- a/src/PerfView/PerfViewData.cs +++ b/src/PerfView/PerfViewData.cs @@ -3770,6 +3770,11 @@ public override void Open(Window parentWindow, StatusBar worker, Action doAfter) // For .NET, we are looking for a Gen 2 GC Start that is induced that has GCBulkNodes after it. var lastGCStartsRelMSec = new Dictionary(); + source.Clr.GCGenAwareStart += delegate (Microsoft.Diagnostics.Tracing.Parsers.Clr.GenAwareBeginTraceData data) + { + lastGCStartsRelMSec[data.ProcessID] = data.TimeStampRelativeMSec; + }; + source.Clr.GCStart += delegate (Microsoft.Diagnostics.Tracing.Parsers.Clr.GCStartTraceData data) { // Look for induced GCs. and remember their when it happened. diff --git a/src/TraceEvent/Parsers/ClrTraceEventParser.cs b/src/TraceEvent/Parsers/ClrTraceEventParser.cs index 17c12cb66..d8c0fd981 100644 --- a/src/TraceEvent/Parsers/ClrTraceEventParser.cs +++ b/src/TraceEvent/Parsers/ClrTraceEventParser.cs @@ -247,6 +247,34 @@ internal ClrTraceEventParserState State } } + public event Action GCGenAwareStart + { + add + { + // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + RegisterTemplate(GenAwareBeginTemplate(value)); + } + remove + { + source.UnregisterEventTemplate(value, 206, ProviderGuid); + source.UnregisterEventTemplate(value, 206, GCTaskGuid); + } + } + + public event Action GCGenAwareEnd + { + add + { + // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + RegisterTemplate(GenAwareEndTemplate(value)); + } + remove + { + source.UnregisterEventTemplate(value, 207, ProviderGuid); + source.UnregisterEventTemplate(value, 207, GCTaskGuid); + } + } + public event Action GCStart { add @@ -1998,6 +2026,14 @@ static private MethodJitMemoryAllocatedForCodeTraceData MethodMemoryAllocatedFor { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName return new MethodJitMemoryAllocatedForCodeTraceData(action, 146, 9, "Method", Guid.Empty, 103, "MemoryAllocatedForJitCode", ProviderGuid, ProviderName); } + static private GenAwareBeginTraceData GenAwareBeginTemplate(Action action) + { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + return new GenAwareBeginTraceData(action, 206, 1, "GC", GCTaskGuid, 1, "GenAwareStart", ProviderGuid, ProviderName); + } + static private GenAwareEndTraceData GenAwareEndTemplate(Action action) + { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + return new GenAwareEndTraceData(action, 207, 1, "GC", GCTaskGuid, 2, "GenAwareEnd", ProviderGuid, ProviderName); + } static private R2RGetEntryPointStartTraceData R2RGetEntryPointStartTemplate(Action action) { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName return new R2RGetEntryPointStartTraceData(action, 160, 9, "Method", MethodTaskGuid, 33, "R2RGetEntryPointStart", ProviderGuid, ProviderName); @@ -2016,7 +2052,7 @@ protected internal override void EnumerateTemplates(Func target, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) + : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName) + { + m_target = target; + } + protected internal override void Dispatch() + { + m_target(this); + } + protected internal override void Validate() + { + Debug.Assert(!(Version == 0 && EventDataLength != 6)); + } + protected internal override Delegate Target + { + get { return m_target; } + set { m_target = (Action)value; } + } + public override StringBuilder ToXml(StringBuilder sb) + { + Prefix(sb); + XmlAttrib(sb, "Count", Count); + XmlAttrib(sb, "ClrInstanceID", ClrInstanceID); + sb.Append("/>"); + return sb; + } + + public override string[] PayloadNames + { + get + { + if (payloadNames == null) + payloadNames = new string[] { "Count", "ClrInstanceID" }; + return payloadNames; + } + } + + public override object PayloadValue(int index) + { + switch (index) + { + case 0: + return Count; + case 1: + return ClrInstanceID; + default: + Debug.Assert(false, "Bad field index"); + return null; + } + } + + private event Action m_target; + #endregion + } + public sealed class GenAwareEndTraceData : TraceEvent + { + public int Count { get { return GetInt32At(0); } } + public int ClrInstanceID { get { return GetInt16At(4); } } + + #region Private + internal GenAwareEndTraceData(Action target, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) + : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName) + { + m_target = target; + } + protected internal override void Dispatch() + { + m_target(this); + } + protected internal override void Validate() + { + Debug.Assert(!(Version == 0 && EventDataLength != 6)); + } + protected internal override Delegate Target + { + get { return m_target; } + set { m_target = (Action)value; } + } + public override StringBuilder ToXml(StringBuilder sb) + { + Prefix(sb); + XmlAttrib(sb, "Count", Count); + XmlAttrib(sb, "ClrInstanceID", ClrInstanceID); + sb.Append("/>"); + return sb; + } + + public override string[] PayloadNames + { + get + { + if (payloadNames == null) + payloadNames = new string[] { "Count", "ClrInstanceID" }; + return payloadNames; + } + } + + public override object PayloadValue(int index) + { + switch (index) + { + case 0: + return Count; + case 1: + return ClrInstanceID; + default: + Debug.Assert(false, "Bad field index"); + return null; + } + } + + private event Action m_target; + #endregion + } public sealed class GCStartTraceData : TraceEvent { public int Count { get { return GetInt32At(0); } }