예제 #1
0
    /// <summary>
    /// Reads the Nodes Element
    /// </summary>
    private static void ReadNodesFromXml(XmlReader reader, MemoryGraph graph)
    {
        Debug.Assert(reader.NodeType == XmlNodeType.Element);
        var inputDepth = reader.Depth;

        reader.Read();      // Advance to children

        var children    = new GrowableArray <NodeIndex>(1000);
        var typeStorage = graph.AllocTypeNodeStorage();

        while (inputDepth < reader.Depth)
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                switch (reader.Name)
                {
                case "Node":
                {
                    NodeIndex     readNodeIndex = (NodeIndex)FetchInt(reader, "Index", -1);
                    NodeTypeIndex typeIndex     = (NodeTypeIndex)FetchInt(reader, "TypeIndex", -1);
                    int           size          = FetchInt(reader, "Size");

                    if (readNodeIndex == NodeIndex.Invalid)
                    {
                        throw new ApplicationException("Node element does not have a Index attribute.");
                    }

                    if (typeIndex == NodeTypeIndex.Invalid)
                    {
                        throw new ApplicationException("Node element does not have a TypeIndex attribute.");
                    }

                    // TODO FIX NOW very inefficient.   Use ReadValueChunk and FastStream to make more efficient.
                    children.Clear();
                    var body = reader.ReadElementContentAsString();
                    foreach (var num in Regex.Split(body, @"\s+"))
                    {
                        if (num.Length > 0)
                        {
                            children.Add((NodeIndex)int.Parse(num));
                        }
                    }

                    if (size == 0)
                    {
                        size = graph.GetType(typeIndex, typeStorage).Size;
                    }

                    // TODO should probably just reserve node index 0 to be an undefined object?
                    NodeIndex nodeIndex = 0;
                    if (readNodeIndex != 0)
                    {
                        nodeIndex = graph.CreateNode();
                    }

                    if (readNodeIndex != nodeIndex)
                    {
                        throw new ApplicationException("Node Indexes do not start at 0 or 1 and increase consecutively.");
                    }

                    graph.SetNode(nodeIndex, typeIndex, size, children);
                }
                break;

                default:
                    Debug.WriteLine("Skipping unknown element {0}", reader.Name);
                    reader.Skip();
                    break;
                }
            }
            else if (!reader.Read())
            {
                break;
            }
        }
    }
예제 #2
0
    public PdbScopeMemoryGraph(string pdbScopeFile)
        : base(10000)
    {
        var children = new GrowableArray <NodeIndex>(1000);
        Dictionary <string, NodeTypeIndex> knownTypes = new Dictionary <string, NodeTypeIndex>(1000);

        XmlReaderSettings settings = new XmlReaderSettings()
        {
            IgnoreWhitespace = true, IgnoreComments = true
        };

        using (XmlReader reader = XmlReader.Create(pdbScopeFile, settings))
        {
            int     foundBestRoot     = int.MaxValue; // it is zero when when we find the best root.
            Address imageBase         = 0;
            uint    sizeOfImageHeader = 0;
            Address lastAddress       = 0;
            int     badValues         = 0;

            Queue <Section> sections     = new Queue <Section>();
            Address         prevAddr     = 0;
            Address         expectedAddr = 0;
            RootIndex = NodeIndex.Invalid;
            NodeIndex firstNodeIndex = NodeIndex.Invalid;
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                    case "Section":
                    {
                        Section section = new Section();
                        section.Start = Address.Parse(reader.GetAttribute("Start"));
                        section.Size  = uint.Parse(reader.GetAttribute("Size"));
                        section.Name  = reader.GetAttribute("Name");
                        sections.Enqueue(section);
                        lastAddress = Math.Max(lastAddress, section.EndRoundedUpToPage);
                    } break;

                    case "Module":
                        if (imageBase == 0)
                        {
                            imageBase         = Address.Parse(reader.GetAttribute("Base"));
                            sizeOfImageHeader = 1024;            // We are using the file size number
                            NodeIndex     nodeIndex = GetNodeIndex(imageBase);
                            NodeTypeIndex typeIndex = CreateType("Image Header");
                            children.Clear();
                            SetNode(nodeIndex, typeIndex, (int)sizeOfImageHeader, children);
                            expectedAddr = imageBase + 0x1000;

                            DebugWriteLine("Loading Module Map table used to decode $N symbol prefixes.");
                            string dllFilePath = reader.GetAttribute("FilePath");
                            if (dllFilePath != null)
                            {
                                LoadModuleMap(dllFilePath, pdbScopeFile);
                            }
                            else
                            {
                                DebugWriteLine("Could not find path to original DLL being analyzed.");
                            }

                            if (m_moduleMap != null)
                            {
                                DebugWriteLine("Loaded Module Map of " + m_moduleMap.Count + " Project N style IL modules to unmangled $N_ prefixes.");
                            }
                            else
                            {
                                DebugWriteLine("Warning: No Module Map Found: $N_ prefixes will not be unmangled.");
                            }
                        }
                        break;

                    case "ObjectTypes":
                    case "Type":
                    case "Dicectory":
                    case "Sections":
                    case "PdbscopeReport":
                    case "Symbols":
                    case "SourceFiles":
                    case "File":
                        break;

                    case "Symbol":
                        string  addrStr = reader.GetAttribute("addr");
                        Address addr;
                        if (addrStr != null && Address.TryParse(addrStr, NumberStyles.AllowHexSpecifier, null, out addr))
                        {
                            if (addr < lastAddress)
                            {
                                // Get Size
                                string sizeStr = reader.GetAttribute("size");
                                uint   size    = 0;
                                if (sizeStr != null)
                                {
                                    uint.TryParse(sizeStr, out size);
                                }

                                // Get Children
                                children.Clear();
                                string to = reader.GetAttribute("to");
                                if (to != null)
                                {
                                    GetChildrenForAddresses(ref children, to);
                                }

                                // Get Name, make a type out of it
                                string        name;
                                NodeTypeIndex typeIndex = GetTypeForXmlElement(knownTypes, reader, size, out name);

                                // Currently PdbScope files have extra information lines where it shows the different generic instantiations associated
                                // with a given symbol.  These ways have the same address as the previous entry and have no size (size will be 0) so
                                // we filter these lines out with the following condition.
                                if (prevAddr != addr || size != 0)
                                {
                                    prevAddr = addr;

                                    if (addr < expectedAddr)
                                    {
                                        DebugWriteLine(string.Format("Got Address {0:x} which is less than the expected address {1:x}.  Discarding {2}",
                                                                     addr, expectedAddr, name));
                                        badValues++;
                                        if (50 < badValues)
                                        {
                                            throw new ApplicationException("Too many cases where the addresses were not ascending in the file");
                                        }
                                        continue;               // discard
                                    }
                                    /*** We want to make sure we account for all bytes, so log when we see gaps ***/
                                    // If we don't match see if it is because of section boundary.
                                    if (addr != expectedAddr)
                                    {
                                        EmitNodesForGaps(sections, expectedAddr, addr);
                                    }

                                    expectedAddr = addr + size;

                                    NodeIndex nodeIndex = GetNodeIndex((Address)addr);
                                    SetNode(nodeIndex, typeIndex, (int)size, children);

                                    // See if this is a good root
                                    if (foundBestRoot != 0 && name != null)
                                    {
                                        if (name == "RHBinder__ShimExeMain")
                                        {
                                            RootIndex     = nodeIndex;
                                            foundBestRoot = 0;
                                        }
                                        else if (0 < foundBestRoot && name.Contains("ILT$Main"))
                                        {
                                            RootIndex     = nodeIndex;
                                            foundBestRoot = 1;
                                        }
                                        else if (1 < foundBestRoot & name.Contains("DllMainCRTStartup"))
                                        {
                                            RootIndex     = nodeIndex;
                                            foundBestRoot = 1;
                                        }
                                        else if (2 < foundBestRoot & name.Contains("Main"))
                                        {
                                            RootIndex     = nodeIndex;
                                            foundBestRoot = 2;
                                        }
                                    }

                                    // Remember first node.
                                    if (firstNodeIndex == NodeIndex.Invalid)
                                    {
                                        firstNodeIndex = nodeIndex;
                                    }
                                }
                            }
                            else
                            {
                                DebugWriteLine(string.Format("Warning Discarding Symbol node {0:x} outside the last address in the image {1:x}", addr, lastAddress));
                            }
                        }
                        else
                        {
                            DebugWriteLine("Error: symbol without addr");
                        }
                        break;

                    default:
                        DebugWriteLine(string.Format("Skipping unknown element {0}", reader.Name));
                        break;
                    }
                }
            }

            EmitNodesForGaps(sections, expectedAddr, lastAddress);

            if (RootIndex == NodeIndex.Invalid)
            {
                RootIndex = firstNodeIndex;
            }
            DebugWriteLine(string.Format("Image Base {0:x} LastAddress {1:x}", imageBase, lastAddress));
            DebugWriteLine(string.Format("Total Virtual Size {0} ({0:x})", lastAddress - imageBase));
            DebugWriteLine(string.Format("Total File Size    {0} ({0:x})", TotalSize));
        }
        AllowReading();
    }
예제 #3
0
    /// <summary>
    /// This demo shows you how to create a a graph of memory and turn it into a stackSource with MemoryGraphStackSource.
    /// </summary>
    public void DemoMemoryGraph()
    {
        // Make a custom stack source that was created out of nothing.   InternStackSouce is your friend here.
        MemoryGraph myGraph = new MemoryGraph(1000);

        // Create a memory graph out of 'nothing'.  In the example below we create a graph where the root which points at
        // 'base' which has a bunch of children, each of which have a child that points back to the base node (thus it has lots of cycles)

        var baseIdx = myGraph.GetNodeIndex(0);                                       // Define the NAME (index) for the of the graph (but we have not defined what is in it)

        GrowableArray <NodeIndex> children     = new GrowableArray <NodeIndex>(1);   // This array is reused again and again for each child.
        GrowableArray <NodeIndex> rootChildren = new GrowableArray <NodeIndex>(100); // This is used to create the children of the root;

        //Here I make up a graph of memory addresses at made up locations
        for (Address objAddr = 0x100000; objAddr < 0x200000; objAddr += 0x10000)
        {
            NodeIndex nodeIdx = myGraph.GetNodeIndex(objAddr);                          // Create the name (node index) for the child
            // Make a type for the child.  In this case we make a new type for each node, normally you keep these in a interning table so that
            // every distinct type name has exactly one type index.   Interning is not STRICTLLY needed, but the representation assumes that
            // there are many more nodes than there are types.
            NodeTypeIndex nodeTypeIdx = myGraph.CreateType("MyType_" + objAddr.ToString(), "MyModule");
            // Create a list of children for this node in this case, each node has exactly one child, which is the root node.
            children.Clear();
            children.Add(baseIdx);
            // Actually define the node with the given name (nodeIdx), type, size (100 in our case) and children;
            myGraph.SetNode(nodeIdx, nodeTypeIdx, 100, children);

            rootChildren.Add(nodeIdx);      // Remember this node name as belonging to the things that the root points at.
        }

        // At this point we have everything we need to define the base node, do it here.
        myGraph.SetNode(baseIdx, myGraph.CreateType("[Base]"), 0, rootChildren);

        // Create a root node that points at the base node.
        myGraph.RootIndex = myGraph.GetNodeIndex(1);
        children.Clear();
        children.Add(baseIdx);
        myGraph.SetNode(myGraph.RootIndex, myGraph.CreateType("[ROOT]"), 0, children);

        // Note that the raw graph APIs force you to know all the children of a particular node before you can define
        // the node.  There is a class call MemoryNodeBuilder which makes this a bit nicer in that you can incrementally
        // add children to nodes and then 'finalize' them all at once at the end.   Ideally, however you don't have many
        // nodes that need MemoryNodeBuilder as they are more expensive to build.

        // So far, the graph is in 'write mode' where only creation APIs are allowed.   Change to 'Read Mode' which no writes
        // are allowed by read APIs are allowed.  (We may lift this restriction at some point).
        myGraph.AllowReading();

        // I can  dump it as XML to look at it.
        using (var writer = File.CreateText("MyGraph.dump.xml"))
        {
            myGraph.WriteXml(writer);
        }

        // I can write the graph out as a file and read it back in later.
        // myGraph.WriteAsBinaryFile("myGraph.gcGraph");
        // var roundTrip = MemoryGraph.ReadFromBinaryFile("myGraph.gcGraph");

        // OK we have a graph, turn it into a stack source so that I can view it.
        MemoryGraphStackSource myStackSource = new MemoryGraphStackSource(myGraph, LogFile);

        // Create a Stacks class (which remembers all the Filters, Scaling policy and other things the viewer wants.
        Stacks stacks = new Stacks(myStackSource);

        // If you wanted to, you can change the filtering ...
        stacks.Filter.GroupRegExs             = "";
        stacks.Filter.MinInclusiveTimePercent = "";

        // And view it.
        OpenStackViewer(stacks);
    }
예제 #4
0
        void Read(TextReader reader)
        {
            var framePattern = new Regex(@"\b(\w+?)\!(\S\(?[\S\s]*\)?)");
            var stackStart   = new Regex(@"Call Site");

            // the call stack from the debugger kc command looksl like this
            //Call Site
            //coreclr!JIT_MonEnterWorker_Portable
            //System_Windows_ni!MS.Internal.ManagedPeerTable.TryGetManagedPeer(IntPtr, Boolean, System.Object ByRef)
            //System_Windows_ni!MS.Internal.ManagedPeerTable.EnsureManagedPeer(IntPtr, Int32, System.Type, Boolean)
            //System_Windows_ni!MS.Internal.FrameworkCallbacks.CheckPeerType(IntPtr, System.String, Boolean)
            //System_Windows_ni!DomainBoundILStubClass.IL_STUB_ReversePInvoke(Int32, IntPtr, Int32)
            //coreclr!UM2MThunk_WrapperHelper
            //coreclr!UMThunkStubWorker
            //coreclr!UMThunkStub
            //agcore!CParser::StartObjectElement
            //agcore!CParser::Attribute
            //agcore!CParser::LoadXaml

            var   stack             = new GrowableArray <DebuggerCallStackFrame>();
            bool  newCallStackFound = false;
            var   sample            = new StackSourceSample(this);
            float time = 0;

            for (; ;)
            {
                var line = reader.ReadLine();
                if (line == null)
                {
                    break;
                }
                var match = framePattern.Match(line);
                if (match.Success && newCallStackFound)
                {
                    var module     = match.Groups[1].Value;
                    var methodName = match.Groups[2].Value;

                    // trim the methodName if it has file name info (if the trace is collected with kv instead of kc)
                    int index = methodName.LastIndexOf(")+");
                    if (index != -1)
                    {
                        methodName = methodName.Substring(0, index + 1);
                    }


                    var moduleIndex = Interner.ModuleIntern(module);
                    var frameIndex  = Interner.FrameIntern(methodName, moduleIndex);

                    DebuggerCallStackFrame frame = new DebuggerCallStackFrame();
                    frame.frame = frameIndex;
                    stack.Add(frame);
                }
                else
                {
                    var stackStartMatch = stackStart.Match(line);
                    if (stackStartMatch.Success)
                    {
                        // start a new sample.
                        // add the previous sample
                        // clear the stack
                        if (stack.Count != 0)
                        {
                            StackSourceCallStackIndex parent = StackSourceCallStackIndex.Invalid;
                            for (int i = stack.Count - 1; i >= 0; --i)
                            {
                                parent = Interner.CallStackIntern(stack[i].frame, parent);
                            }
                            stack.Clear();

                            sample.StackIndex       = parent;
                            sample.TimeRelativeMSec = time;
                            time++;
                            AddSample(sample);
                        }
                        newCallStackFound = true;
                    }
                }
            }
            Interner.DoneInterning();
        }
    /// <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;
    }
예제 #6
0
        public GCHeapGraph(GCHeap heap)
            : base((int)heap.NumberOfObjects)
        {
            // TODO is basically nodes that have not had 'SetNode' done to them.  Thus the node index has been
            // allocated, but we have not processed the defintion of the node.
            // This list should NEVER contain duplicates (you should test if you already processed the node before
            // adding to this queue.
            Queue <NodeIndex> toDo = new Queue <NodeIndex>();

            // Create the root node.
            var root = new MemoryNodeBuilder(this, "[root]");

            // Create categories for the roots.
            var nodeForKind = new MemoryNodeBuilder[((int)GCRootKind.Max) + 1];

            var otherRoots = root.FindOrCreateChild("[other roots]");

            nodeForKind[(int)GCRootKind.LocalVar] = otherRoots.FindOrCreateChild("[unknown local vars]");
            for (int i = 0; i < nodeForKind.Length; i++)
            {
                if (nodeForKind[i] == null)
                {
                    nodeForKind[i] = otherRoots.FindOrCreateChild("[" + ((GCRootKind)i).ToString().ToLower() + " handles]");
                }
            }
            var unreachable = root.FindOrCreateChild("[unreachable but not yet collected]");

            foreach (var gcRoot in heap.Roots)
            {
                if (gcRoot.HeapReference == 0)
                {
                    continue;
                }

                // TODO FIX NOW condition should not be needed, should be able to assert it.
                if (!heap.IsInHeap(gcRoot.HeapReference))
                {
                    continue;
                }
                Debug.Assert(heap.IsInHeap(gcRoot.HeapReference));

                // Make a node for it, and notice if the root is already in the heap.
                var lastLimit  = NodeIndexLimit;
                var newNodeIdx = GetNodeIndex(gcRoot.HeapReference);

                var nodeForGcRoot = nodeForKind[(int)gcRoot.Kind];
                if (gcRoot.Type != null)
                {
                    var prefix = "other";
                    if (gcRoot.Kind == GCRootKind.StaticVar)
                    {
                        prefix = "static";
                    }
                    else if (gcRoot.Kind == GCRootKind.LocalVar)
                    {
                        prefix = "local";
                    }
                    var appDomainNode = root.FindOrCreateChild("[appdomain " + gcRoot.AppDomainName + "]");
                    var varKindNode   = appDomainNode.FindOrCreateChild("[" + prefix + " vars]");
                    var moduleName    = Path.GetFileNameWithoutExtension(gcRoot.Type.ModuleFilePath);
                    var moduleNode    = varKindNode.FindOrCreateChild("[" + prefix + " vars " + moduleName + "]");
                    var typeNode      = moduleNode.FindOrCreateChild("[" + prefix + " vars " + gcRoot.Type.Name + "]");
                    var name          = gcRoot.Type.Name + "+" + gcRoot.Name + " [" + prefix + " var]";
                    nodeForGcRoot = typeNode.FindOrCreateChild(name, gcRoot.Type.ModuleFilePath);

                    // TODO REMOVE
                    // if (nodeForGcRoot.Size == 0)
                    //    nodeForGcRoot.Size = 4;
                }

                // Give the user a bit more information about runtime internal handles
                if (gcRoot.Kind == GCRootKind.Pinning)
                {
                    var pinnedType = heap.GetObjectType(gcRoot.HeapReference);
                    if (pinnedType.Name == "System.Object []")
                    {
                        // Traverse pinned object arrays a bit 'manually' here so we tell the users they are likely handles.
                        var handleArray = new MemoryNodeBuilder(
                            this, "[likely runtime object array handle table]", pinnedType.ModuleFilePath, newNodeIdx);
                        handleArray.Size = pinnedType.GetSize(gcRoot.HeapReference);
                        nodeForGcRoot.AddChild(handleArray);

                        pinnedType.EnumerateRefsOfObjectCarefully(gcRoot.HeapReference, delegate(Address childRef, int childFieldOffset)
                        {
                            // Poor man's visited bit.  If we did not need to add a node, then we have visited it.
                            var childLastLimit = NodeIndexLimit;
                            var childNodeIdx   = GetNodeIndex(childRef);
                            if (childNodeIdx < childLastLimit)       // Already visited, simply add the child and move one
                            {
                                handleArray.AddChild(childNodeIdx);
                                return;
                            }

                            var childType = heap.GetObjectType(childRef);
                            if (childType.Name == "System.String")
                            {
                                // TODO FIX NOW:  Only want to morph the name if something else does not point at it.
                                var literal = new MemoryNodeBuilder(
                                    this, "[likely string literal]", childType.ModuleFilePath, childNodeIdx);
                                literal.Size = childType.GetSize(childRef);
                                handleArray.AddChild(literal);
                            }
                            else
                            {
                                handleArray.AddChild(childNodeIdx);
                                toDo.Enqueue(childNodeIdx);
                            }
                        });
                        continue;       // we are done processing this node.
                    }
                }

                nodeForGcRoot.AddChild(newNodeIdx);
                if (newNodeIdx >= lastLimit)      // have not been visited.
                {
                    toDo.Enqueue(newNodeIdx);
                }
            }

            root.AllocateTypeIndexes();

            // Create the necessary types.
            int memoryTypesStart = (int)NodeTypeIndexLimit;

            foreach (var type in heap.Types)
            {
                NodeTypeIndex nodeType = CreateType(type.Name, type.ModuleFilePath);
                Debug.Assert((int)nodeType == (int)type.Index + memoryTypesStart);
            }

            var children = new GrowableArray <NodeIndex>();

            while (toDo.Count > 0)
            {
                var nodeIndex = toDo.Dequeue();
                var objRef    = GetAddress(nodeIndex);

                children.Clear();
                GCHeapType objType = heap.GetObjectType(objRef);
                objType.EnumerateRefsOfObjectCarefully(objRef, delegate(Address childRef, int childFieldOffset)
                {
                    Debug.Assert(heap.IsInHeap(childRef));
                    var lastLimit    = NodeIndexLimit;
                    var childNodeIdx = GetNodeIndex(childRef);
                    children.Add(childNodeIdx);

                    // Poor man's visited bit.  If the index we just asked for is a new node, put it in the work queue
                    if (childNodeIdx >= lastLimit)
                    {
                        toDo.Enqueue(childNodeIdx);
                    }
                });

                int objSize = objType.GetSize(objRef);
                Debug.Assert(objSize > 0);
                SetNode(nodeIndex, (NodeTypeIndex)((int)objType.Index + memoryTypesStart), objSize, children);
            }

            long unreachableSize = (heap.TotalSize - TotalSize);

            unreachable.Size = (int)unreachableSize;
            if (unreachable.Size != unreachableSize)
            {
                unreachable.Size = int.MaxValue;        // TODO not correct on overflow
            }
            // We are done!
            RootIndex = root.Build();
            AllowReading();
        }