public RCWInfo(GCBulkRCWValues data) { IUnknown = data.IUnknown; }
/// <summary> /// Sets up the callbacks needed to do a heap dump (work need before processing the events() /// </summary> internal void SetupCallbacks(MemoryGraph memoryGraph, TraceEventDispatcher source, string processNameOrId = null, double startTimeRelativeMSec = 0) { m_graph = memoryGraph; m_typeID2TypeIndex = new Dictionary <Address, NodeTypeIndex>(1000); m_moduleID2Name = new Dictionary <Address, string>(16); m_arrayNametoIndex = new Dictionary <string, NodeTypeIndex>(32); m_objectToRCW = new Dictionary <Address, RCWInfo>(100); m_nodeBlocks = new Queue <GCBulkNodeTraceData>(); m_edgeBlocks = new Queue <GCBulkEdgeTraceData>(); m_typeBlocks = new Queue <GCBulkTypeTraceData>(); m_staticVarBlocks = new Queue <GCBulkRootStaticVarTraceData>(); m_ccwBlocks = new Queue <GCBulkRootCCWTraceData>(); m_typeIntern = new Dictionary <string, NodeTypeIndex>(); m_root = new MemoryNodeBuilder(m_graph, "[.NET Roots]"); m_typeStorage = m_graph.AllocTypeNodeStorage(); // We also keep track of the loaded modules in the target process just in case it is a project N scenario. // (Not play for play but it is small). m_modules = new Dictionary <Address, Module>(32); m_ignoreEvents = true; m_ignoreUntilMSec = startTimeRelativeMSec; m_processId = 0; // defaults to a wildcard. if (processNameOrId != null) { if (!int.TryParse(processNameOrId, out m_processId)) { m_processId = -1; // an illegal value. m_processName = processNameOrId; } } // Remember the module IDs too. Action <ModuleLoadUnloadTraceData> moduleCallback = delegate(ModuleLoadUnloadTraceData data) { if (data.ProcessID != m_processId) { return; } if (!m_moduleID2Name.ContainsKey((Address)data.ModuleID)) { m_moduleID2Name[(Address)data.ModuleID] = data.ModuleILPath; } m_log.WriteLine("Found Module {0} ID 0x{1:x}", data.ModuleILFileName, (Address)data.ModuleID); }; source.Clr.AddCallbackForEvents <ModuleLoadUnloadTraceData>(moduleCallback); // Get module events for clr provider // TODO should not be needed if we use CAPTURE_STATE when collecting. var clrRundown = new ClrRundownTraceEventParser(source); clrRundown.AddCallbackForEvents <ModuleLoadUnloadTraceData>(moduleCallback); // and its rundown provider. DbgIDRSDSTraceData lastDbgData = null; var symbolParser = new SymbolTraceEventParser(source); symbolParser.ImageIDDbgID_RSDS += delegate(DbgIDRSDSTraceData data) { if (data.ProcessID != m_processId) { return; } lastDbgData = (DbgIDRSDSTraceData)data.Clone(); }; source.Kernel.ImageGroup += delegate(ImageLoadTraceData data) { if (m_processId == 0) { return; } if (data.ProcessID != m_processId) { return; } Module module = new Module(data.ImageBase); module.Path = data.FileName; module.Size = data.ImageSize; module.BuildTime = data.BuildTime; if (lastDbgData != null && data.TimeStampRelativeMSec == lastDbgData.TimeStampRelativeMSec) { module.PdbGuid = lastDbgData.GuidSig; module.PdbAge = lastDbgData.Age; module.PdbName = lastDbgData.PdbFileName; } m_modules[module.ImageBase] = module; }; // TODO this does not work in the circular case source.Kernel.ProcessGroup += delegate(ProcessTraceData data) { if (0 <= m_processId || m_processName == null) { return; } if (string.Compare(data.ProcessName, processNameOrId, StringComparison.OrdinalIgnoreCase) == 0) { m_log.WriteLine("Found process id {0} for process Name {1}", processNameOrId, data.ProcessName); m_processId = data.ProcessID; } else { m_log.WriteLine("Found process {0} but does not match {1}", data.ProcessName, processNameOrId); } }; source.Clr.GCStart += delegate(GCStartTraceData data) { // If this GC is not part of a heap dump, ignore it. // TODO FIX NOW if (data.ClientSequenceNumber == 0) // return; if (data.TimeStampRelativeMSec < m_ignoreUntilMSec) { return; } if (m_processId == 0) { m_processId = data.ProcessID; m_log.WriteLine("Process wildcard selects process id {0}", m_processId); } if (data.ProcessID != m_processId) { m_log.WriteLine("GC Start found but Process ID {0} != {1} desired ID", data.ProcessID, m_processId); return; } if (!IsProjectN && data.ProviderGuid == ClrTraceEventParser.NativeProviderGuid) { IsProjectN = true; } if (data.Depth < 2 || data.Type != GCType.NonConcurrentGC) { m_log.WriteLine("GC Start found but not a Foreground Gen 2 GC"); return; } if (data.Reason != GCReason.Induced) { m_log.WriteLine("GC Start not induced. Skipping."); return; } if (!m_seenStart) { m_gcID = data.Count; m_log.WriteLine("Found a Gen2 Induced non-background GC Start at {0:n3} msec GC Count {1}", data.TimeStampRelativeMSec, m_gcID); m_ignoreEvents = false; m_seenStart = true; memoryGraph.Is64Bit = (data.PointerSize == 8); } }; source.Clr.GCStop += delegate(GCEndTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) { return; } if (data.Count == m_gcID) { m_log.WriteLine("Found a GC Stop at {0:n3} for GC {1}, ignoring events from now on.", data.TimeStampRelativeMSec, m_gcID); 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; } // TODO we have to continue processing to get the module rundown events. // If we could be sure to get these early, we could optimized this. // source.StopProcessing(); } else { m_log.WriteLine("Found a GC Stop at {0:n3} but id {1} != {2} Target ID", data.TimeStampRelativeMSec, data.Count, m_gcID); } }; 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. // This means that when setting m_processId to 0 in the command line may still lose type events. if (data.ProcessID != m_processId) { return; } m_typeBlocks.Enqueue((GCBulkTypeTraceData)data.Clone()); }; source.Clr.GCBulkNode += delegate(GCBulkNodeTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) { return; } m_nodeBlocks.Enqueue((GCBulkNodeTraceData)data.Clone()); }; source.Clr.GCBulkEdge += delegate(GCBulkEdgeTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) { return; } m_edgeBlocks.Enqueue((GCBulkEdgeTraceData)data.Clone()); }; source.Clr.GCBulkRootEdge += delegate(GCBulkRootEdgeTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) { return; } MemoryNodeBuilder staticRoot = m_root.FindOrCreateChild("[static vars]"); for (int i = 0; i < data.Count; i++) { var value = data.Values(i); var flags = value.GCRootFlag; if ((flags & GCRootFlags.WeakRef) == 0) // ignore weak references. they are not roots. { GCRootKind kind = value.GCRootKind; MemoryNodeBuilder root = m_root; string name; if (kind == GCRootKind.Stack) { name = "[local vars]"; } else { root = m_root.FindOrCreateChild("[other roots]"); if ((flags & GCRootFlags.RefCounted) != 0) { name = "[COM/WinRT Objects]"; } else if (kind == GCRootKind.Finalizer) { name = "[finalizer Handles]"; } else if (kind == GCRootKind.Handle) { if (flags == GCRootFlags.Pinning) { name = "[pinning Handles]"; } else { name = "[strong Handles]"; } } else { name = "[other Handles]"; } // Remember the root for later processing. if (value.RootedNodeAddress != 0) { Address gcRootId = value.GCRootID; if (gcRootId != 0 && IsProjectN) { Module gcRootModule = GetModuleForAddress(gcRootId); if (gcRootModule != null) { var staticRva = (int)(gcRootId - gcRootModule.ImageBase); var staticTypeIdx = m_graph.CreateType(staticRva, gcRootModule, 0, " (static var)"); var staticNodeIdx = m_graph.CreateNode(); m_children.Clear(); m_children.Add(m_graph.GetNodeIndex(value.RootedNodeAddress)); m_graph.SetNode(staticNodeIdx, staticTypeIdx, 0, m_children); staticRoot.AddChild(staticNodeIdx); Trace.WriteLine("Got Static 0x" + gcRootId.ToString("x") + " pointing at 0x" + value.RootedNodeAddress.ToString("x") + " kind " + value.GCRootKind + " flags " + value.GCRootFlag); continue; } } Trace.WriteLine("Got GC Root 0x" + gcRootId.ToString("x") + " pointing at 0x" + value.RootedNodeAddress.ToString("x") + " kind " + value.GCRootKind + " flags " + value.GCRootFlag); } } root = root.FindOrCreateChild(name); Address objId = value.RootedNodeAddress; root.AddChild(m_graph.GetNodeIndex(objId)); } } }; source.Clr.GCBulkRCW += delegate(GCBulkRCWTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) { return; } for (int i = 0; i < data.Count; i++) { GCBulkRCWValues comInfo = data.Values(i); m_objectToRCW[comInfo.ObjectID] = new RCWInfo(comInfo); } }; source.Clr.GCBulkRootCCW += delegate(GCBulkRootCCWTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) { return; } m_ccwBlocks.Enqueue((GCBulkRootCCWTraceData)data.Clone()); }; source.Clr.GCBulkRootStaticVar += delegate(GCBulkRootStaticVarTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) { return; } m_staticVarBlocks.Enqueue((GCBulkRootStaticVarTraceData)data.Clone()); }; source.Clr.GCBulkRootConditionalWeakTableElementEdge += delegate(GCBulkRootConditionalWeakTableElementEdgeTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) { return; } var otherRoots = m_root.FindOrCreateChild("[other roots]"); var dependentHandles = otherRoots.FindOrCreateChild("[Dependent Handles]"); for (int i = 0; i < data.Count; i++) { var value = data.Values(i); // TODO fix this so that they you see this as an arc from source to target. // The target is alive only if the source ID (which is a weak handle) is alive (non-zero) if (value.GCKeyNodeID != 0) { dependentHandles.AddChild(m_graph.GetNodeIndex(value.GCValueNodeID)); } } }; source.Clr.GCGenerationRange += delegate(GCGenerationRangeTraceData data) { if (m_ignoreEvents || data.ProcessID != m_processId) { return; } if (m_dotNetHeapInfo == null) { return; } // We want the 'after' ranges so we wait if (m_nodeBlocks.Count == 0) { return; } Address start = data.RangeStart; Address end = start + data.RangeUsedLength; if (m_dotNetHeapInfo.Segments == null) { m_dotNetHeapInfo.Segments = new List <GCHeapDumpSegment>(); } GCHeapDumpSegment segment = new GCHeapDumpSegment(); segment.Start = start; segment.End = end; switch (data.Generation) { case 0: segment.Gen0End = end; break; case 1: segment.Gen1End = end; break; case 2: segment.Gen2End = end; break; case 3: segment.Gen3End = end; break; default: throw new Exception("Invalid generation in GCGenerationRangeTraceData"); } m_dotNetHeapInfo.Segments.Add(segment); }; }