private void OnTypeBulkType(GCBulkTypeTraceData data) { if (data.ProcessID != _processId) { return; } // keep track of the id/name type associations for (int currentType = 0; currentType < data.Count; currentType++) { GCBulkTypeValues value = data.Values(currentType); _types[value.TypeID] = string.Intern(value.TypeName); } }
private void OnTypeBulkType(GCBulkTypeTraceData data) { if (FilterOutEvent(data)) { return; } ProcessTypeMapping mapping = GetProcessTypesMapping(data.ProcessID); for (int currentType = 0; currentType < data.Count; currentType++) { GCBulkTypeValues value = data.Values(currentType); mapping[value.TypeID] = value.TypeName; } }
private void OnEtwClassIDDefintion(GCBulkTypeTraceData data) { if (data.ProcessID != m_processID) { return; } if (m_useEtlClrProfilerEvents) { return; } for (int i = 0; i < data.Count; i++) { GCBulkTypeValues typeData = data.Values(i); var typeName = typeData.TypeName; if (typeData.TypeParameterCount != 0) { typeName += "<"; for (int j = 0; j < typeData.TypeParameterCount; j++) { if (j != 0) { typeName += ","; } TypeInfo paramInfo; if (m_classNamesAsFrames.TryGetValue(typeData.TypeParameterID(j), out paramInfo)) { typeName += paramInfo.TypeName; } } typeName += ">"; } if (typeData.CorElementType == 0x1d) // SZArray { typeName += "[]"; } // TODO FIX NOW make sure the COR_ELEMENT_TYPES are covered. m_classNamesAsFrames[typeData.TypeID] = new TypeInfo() { TypeName = typeName, FrameIdx = m_stackSource.Interner.FrameIntern("Type " + typeName) }; } }
/// <summary> /// After reading the events the graph is not actually created, you need to post process the information we gathered /// from the events. This is where that happens. Thus 'SetupCallbacks, Process(), ConvertHeapDataToGraph()' is how /// you dump a heap. /// </summary> internal unsafe void ConvertHeapDataToGraph() { int maxNodeCount = 10_000_000; if (m_converted) { return; } m_converted = true; if (!m_seenStart) { if (m_processName != null) { throw new ApplicationException("ETL file did not include a Heap Dump for process " + m_processName); } throw new ApplicationException("ETL file did not include a Heap Dump for process ID " + m_processId); } if (!m_ignoreEvents) { throw new ApplicationException("ETL file shows the start of a heap dump but not its completion."); } m_log.WriteLine("Processing Heap Data, BulkTypeEventCount:{0} BulkNodeEventCount:{1} BulkEdgeEventCount:{2}", m_typeBlocks.Count, m_nodeBlocks.Count, m_edgeBlocks.Count); // Process the type information (we can't do it on the fly because we need the module information, which may be // at the end of the trace. while (m_typeBlocks.Count > 0) { GCBulkTypeTraceData data = m_typeBlocks.Dequeue(); for (int i = 0; i < data.Count; i++) { GCBulkTypeValues typeData = data.Values(i); var typeName = typeData.TypeName; if (IsProjectN) { // For project N we only log the type ID and module base address. Debug.Assert(typeName.Length == 0); Debug.Assert((typeData.Flags & TypeFlags.ModuleBaseAddress) != 0); var moduleBaseAddress = typeData.TypeID - (ulong)typeData.TypeNameID; // Tricky way of getting the image base. Debug.Assert((moduleBaseAddress & 0xFFFF) == 0); // Image loads should be on 64K boundaries. Module module = GetModuleForImageBase(moduleBaseAddress); if (module.Path == null) { m_log.WriteLine("Error: Could not find DLL name for imageBase 0x{0:x} looking up typeID 0x{1:x} with TypeNameID {2:x}", moduleBaseAddress, typeData.TypeID, typeData.TypeNameID); } m_typeID2TypeIndex[typeData.TypeID] = m_graph.CreateType(typeData.TypeNameID, module); } else { if (typeName.Length == 0) { if ((typeData.Flags & TypeFlags.Array) != 0) { typeName = "ArrayType(0x" + typeData.TypeNameID.ToString("x") + ")"; } else { typeName = "Type(0x" + typeData.TypeNameID.ToString("x") + ")"; } } // TODO FIX NOW these are kind of hacks typeName = Regex.Replace(typeName, @"`\d+", ""); typeName = typeName.Replace("[", "<"); typeName = typeName.Replace("]", ">"); typeName = typeName.Replace("<>", "[]"); string moduleName; if (!m_moduleID2Name.TryGetValue(typeData.ModuleID, out moduleName)) { moduleName = "Module(0x" + typeData.ModuleID.ToString("x") + ")"; m_moduleID2Name[typeData.ModuleID] = moduleName; } // Is this type a an RCW? If so mark the type name that way. if ((typeData.Flags & TypeFlags.ExternallyImplementedCOMObject) != 0) { typeName = "[RCW " + typeName + "]"; } m_typeID2TypeIndex[typeData.TypeID] = CreateType(typeName, moduleName); // Trace.WriteLine(string.Format("Type 0x{0:x} = {1}", typeData.TypeID, typeName)); } } } // Process all the ccw root information (which also need the type information complete) var ccwRoot = m_root.FindOrCreateChild("[COM/WinRT Objects]"); while (m_ccwBlocks.Count > 0) { GCBulkRootCCWTraceData data = m_ccwBlocks.Dequeue(); GrowableArray <NodeIndex> ccwChildren = new GrowableArray <NodeIndex>(1); for (int i = 0; i < data.Count; i++) { unsafe { GCBulkRootCCWValues ccwInfo = data.Values(i); // TODO Debug.Assert(ccwInfo.IUnknown != 0); if (ccwInfo.IUnknown == 0) { // TODO currently there are times when a CCWs IUnknown pointer is not set (it is set lazily). // m_log.WriteLine("Warning seen a CCW with IUnknown == 0"); continue; } // Create a CCW node that represents the COM object that has one child that points at the managed object. var ccwNode = m_graph.GetNodeIndex(ccwInfo.IUnknown); var ccwTypeIndex = GetTypeIndex(ccwInfo.TypeID, 200); var ccwType = m_graph.GetType(ccwTypeIndex, m_typeStorage); var typeName = "[CCW 0x" + ccwInfo.IUnknown.ToString("x") + " for type " + ccwType.Name + "]"; ccwTypeIndex = CreateType(typeName); ccwChildren.Clear(); ccwChildren.Add(m_graph.GetNodeIndex(ccwInfo.ObjectID)); m_graph.SetNode(ccwNode, ccwTypeIndex, 200, ccwChildren); ccwRoot.AddChild(ccwNode); } } } // Process all the static variable root information (which also need the module information complete var staticVarsRoot = m_root.FindOrCreateChild("[static vars]"); while (m_staticVarBlocks.Count > 0) { GCBulkRootStaticVarTraceData data = m_staticVarBlocks.Dequeue(); for (int i = 0; i < data.Count; i++) { GCBulkRootStaticVarValues staticVarData = data.Values(i); var rootToAddTo = staticVarsRoot; if ((staticVarData.Flags & GCRootStaticVarFlags.ThreadLocal) != 0) { rootToAddTo = m_root.FindOrCreateChild("[thread static vars]"); } // Get the type name. NodeTypeIndex typeIdx; string typeName; if (m_typeID2TypeIndex.TryGetValue(staticVarData.TypeID, out typeIdx)) { var type = m_graph.GetType(typeIdx, m_typeStorage); typeName = type.Name; } else { typeName = "Type(0x" + staticVarData.TypeID.ToString("x") + ")"; } string fullFieldName = typeName + "." + staticVarData.FieldName; rootToAddTo = rootToAddTo.FindOrCreateChild("[static var " + fullFieldName + "]"); var nodeIdx = m_graph.GetNodeIndex(staticVarData.ObjectID); rootToAddTo.AddChild(nodeIdx); } } // var typeStorage = m_graph.AllocTypeNodeStorage(); GCBulkNodeUnsafeNodes nodeStorage = new GCBulkNodeUnsafeNodes(); // Process all the node and edge nodes we have collected. bool doCompletionCheck = true; for (; ;) { GCBulkNodeUnsafeNodes *node = GetNextNode(&nodeStorage); if (node == null) { break; } // Get the node index var nodeIdx = m_graph.GetNodeIndex((Address)node->Address); var objSize = (int)node->Size; Debug.Assert(node->Size < 0x1000000000); var typeIdx = GetTypeIndex(node->TypeID, objSize); // TODO FIX NOW REMOVE // var type = m_graph.GetType(typeIdx, typeStorage); // Trace.WriteLine(string.Format("Got Object 0x{0:x} Type {1} Size {2} #children {3} nodeIdx {4}", (Address)node->Address, type.Name, objSize, node->EdgeCount, nodeIdx)); // Process the edges (which can add children) m_children.Clear(); for (int i = 0; i < node->EdgeCount; i++) { Address edge = GetNextEdge(); var childIdx = m_graph.GetNodeIndex(edge); m_children.Add(childIdx); // Trace.WriteLine(string.Format(" Child 0x{0:x}", edge)); } // TODO we can use the nodes type to see if this is an RCW before doing this lookup which may be a bit more efficient. RCWInfo info; if (m_objectToRCW.TryGetValue((Address)node->Address, out info)) { // Add the COM object this RCW points at as a child of this node. m_children.Add(m_graph.GetNodeIndex(info.IUnknown)); // We add 1000 to account for the overhead of the RCW that is NOT on the GC heap. objSize += 1000; } Debug.Assert(!m_graph.IsDefined(nodeIdx)); m_graph.SetNode(nodeIdx, typeIdx, objSize, m_children); if (m_graph.NodeCount >= maxNodeCount) { doCompletionCheck = false; var userMessage = string.Format("Exceeded max node count {0}", maxNodeCount); m_log.WriteLine("[WARNING: ]", userMessage); break; } } if (doCompletionCheck && m_curEdgeBlock != null && m_curEdgeBlock.Count != m_curEdgeIdx) { throw new ApplicationException("Error: extra edge data. Giving up on heap dump."); } m_root.Build(); m_graph.RootIndex = m_root.Index; }