Exemplo n.º 1
0
        static void Main()
        {
            int         expectedNumberOfNodes = 1000;
            MemoryGraph memoryGraph           = new MemoryGraph(expectedNumberOfNodes);

            GrowableArray <NodeIndex> tempForChildren = new GrowableArray <NodeIndex>();


            // We can make a new Node index
            NodeIndex newNodeIdx = memoryGraph.CreateNode();

            NodeIndex childIdx = memoryGraph.CreateNode();


            //
            NodeTypeIndex newNodeType = memoryGraph.CreateType("MyChild");



            memoryGraph.SetNode(childIdx, newType, 100, tempForChildren);



            memoryGraph.AllowReading();

            // Serialize to a file
            memoryGraph.WriteAsBinaryFile("file.gcHeap");



            // Can unserialize easily.
            // var readBackIn = MemoryGraph.ReadFromBinaryFile("file.gcHeap");
        }
Exemplo n.º 2
0
        private NodeTypeIndex GetNodeTypeIndex(ProfilerTypeID typeId)
        {
            var typeIdasInt = (int)typeId;

            NodeTypeIndex ret = NodeTypeIndex.Invalid;

            if (typeIdasInt >= m_profilerTypeToNodeType.Count)
            {
                int prevSize = m_profilerTypeToNodeType.Count;
                int newSize  = typeIdasInt + 100;
                m_profilerTypeToNodeType.Count = newSize;
                for (int i = prevSize; i < newSize; i++)
                {
                    m_profilerTypeToNodeType[i] = NodeTypeIndex.Invalid;
                }
            }
            else
            {
                ret = m_profilerTypeToNodeType[typeIdasInt];
            }

            if (ret == NodeTypeIndex.Invalid)
            {
                ProfilerType profilerType = m_clrProfilerParser.GetTypeById(typeId);
                ret = CreateType(profilerType.name);
                m_profilerTypeToNodeType[typeIdasInt] = ret;

                // TODO FIX NOW don't allocate every time
                GetType(ret, AllocTypeNodeStorage()).ModuleName = profilerType.ModuleName;
            }
            return(ret);
        }
Exemplo n.º 3
0
    private NodeIndex AddLineData(ref LineData lineData)
    {
        NodeIndex     nodeIndex = m_graph.GetNodeIndex(lineData.Offset);
        NodeTypeIndex nodeType  = GetType(lineData.Kind + " " + lineData.Name, lineData.Size);

        m_graph.SetNode(nodeIndex, nodeType, lineData.Size, lineData.Children);
        return(nodeIndex);
    }
        private void SetTypePriorities(string priorityPats)
        {
            if (m_typePriorities == null)
            {
                m_typePriorities = new float[(int)m_graph.NodeTypeIndexLimit];
            }

            string[] priorityPatArray   = priorityPats.Split(';');
            Regex[]  priorityRegExArray = new Regex[priorityPatArray.Length];
            float[]  priorityArray      = new float[priorityPatArray.Length];
            for (int i = 0; i < priorityPatArray.Length; i++)
            {
                var m = Regex.Match(priorityPatArray[i], @"(.*)->(-?\d+.?\d*)");
                if (!m.Success)
                {
                    if (string.IsNullOrWhiteSpace(priorityPatArray[i]))
                    {
                        continue;
                    }

                    throw new ApplicationException("Priority pattern " + priorityPatArray[i] + " is not of the form Pat->Num.");
                }

                var dotNetRegEx = FilterStackSource.ToDotNetRegEx(m.Groups[1].Value.Trim());
                priorityRegExArray[i] = new Regex(dotNetRegEx, RegexOptions.IgnoreCase);
                priorityArray[i]      = float.Parse(m.Groups[2].Value);
            }

            // Assign every type index a priority in m_typePriorities based on if they match a pattern.
            NodeType typeStorage = m_graph.AllocTypeNodeStorage();

            for (NodeTypeIndex typeIdx = 0; typeIdx < m_graph.NodeTypeIndexLimit; typeIdx++)
            {
                var type = m_graph.GetType(typeIdx, typeStorage);

                var fullName = type.Name;
                for (int regExIdx = 0; regExIdx < priorityRegExArray.Length; regExIdx++)
                {
                    var priorityRegEx = priorityRegExArray[regExIdx];
                    if (priorityRegEx == null)
                    {
                        continue;
                    }

                    var m = priorityRegEx.Match(fullName);
                    if (m.Success)
                    {
                        m_typePriorities[(int)typeIdx] = priorityArray[regExIdx];
                        // m_log.WriteLine("Type {0} assigned priority {1:f3}", fullName, priorityArray[regExIdx]);
                        break;
                    }
                }
            }
        }
Exemplo n.º 5
0
    /// <summary>
    /// Reads the NodeTypes element
    /// </summary>
    private static void ReadNodeTypesFromXml(XmlReader reader, MemoryGraph graph)
    {
        Debug.Assert(reader.NodeType == XmlNodeType.Element);
        var inputDepth = reader.Depth;

        reader.Read();      // Advance to children
        while (inputDepth < reader.Depth)
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                switch (reader.Name)
                {
                case "NodeType":
                {
                    NodeTypeIndex readTypeIndex = (NodeTypeIndex)FetchInt(reader, "Index", -1);
                    int           size          = FetchInt(reader, "Size");
                    string        typeName      = reader.GetAttribute("Name");
                    string        moduleName    = reader.GetAttribute("Module");

                    if (typeName == null)
                    {
                        throw new ApplicationException("NodeType element does not have a Name attribute");
                    }

                    if (readTypeIndex == NodeTypeIndex.Invalid)
                    {
                        throw new ApplicationException("NodeType element does not have a Index attribute.");
                    }

                    if (readTypeIndex != 0 || typeName != "UNDEFINED")
                    {
                        NodeTypeIndex typeIndex = graph.CreateType(typeName, moduleName, size);
                        if (readTypeIndex != typeIndex)
                        {
                            throw new ApplicationException("NodeType Indexes do not start at 1 and increase consecutively.");
                        }
                    }
                    reader.Skip();
                }
                break;

                default:
                    Debug.WriteLine("Skipping unknown element {0}", reader.Name);
                    reader.Skip();
                    break;
                }
            }
            else if (!reader.Read())
            {
                break;
            }
        }
    }
Exemplo n.º 6
0
        public override StackSourceFrameIndex GetFrameIndex(StackSourceCallStackIndex callStackIndex)
        {
            NodeIndex nodeIndex = (NodeIndex)callStackIndex;

            // Orphan node support
            if (nodeIndex == m_graph.NodeIndexLimit)
            {
                return((StackSourceFrameIndex)m_graph.NodeTypeIndexLimit);
            }

            NodeTypeIndex typeIndex = m_graph.GetNode(nodeIndex, m_nodeStorage).TypeIndex;

            return((StackSourceFrameIndex)typeIndex);
        }
Exemplo n.º 7
0
    public static MemoryGraph Create(string dllPath, SymbolReader symbolReader)
    {
        var ret = new MemoryGraph(1000);

        string pdbPath = symbolReader.FindSymbolFilePathForModule(dllPath);

        symbolReader.Log.WriteLine("Got PDB path {0}", pdbPath);

        NativeSymbolModule module  = symbolReader.OpenNativeSymbolFile(pdbPath);
        List <Symbol>      symbols = new List <Symbol>();

        AddAllChildren(symbols, module.GlobalSymbol);

        symbols.Sort();

        /****** Make a graph out of the symbols ******/
        // Put all nodes under this root.
        var rootChildren = new GrowableArray <NodeIndex>(1000);

        // Create a node for each symbol
        uint   lastRVA  = 0;
        string lastName = "Header";
        var    empty    = new GrowableArray <NodeIndex>();

        foreach (var symbol in symbols)
        {
            var symRVA   = symbol.RVA;
            int lastSize = (int)symRVA - (int)lastRVA;

            NodeTypeIndex typeIdx = ret.CreateType(lastName, null, lastSize);
            NodeIndex     nodeIdx = ret.CreateNode();
            ret.SetNode(nodeIdx, typeIdx, lastSize, empty);
            rootChildren.Add(nodeIdx);

            lastName = symbol.Name;
            lastRVA  = symRVA;
        }
        // TODO FIX NOW dropping the last symbol.

        // Create the root node.
        NodeIndex     rootIdx     = ret.CreateNode();
        NodeTypeIndex rootTypeIdx = ret.CreateType("METHODS");

        ret.SetNode(rootIdx, rootTypeIdx, 0, rootChildren);
        ret.RootIndex = rootIdx;

        ret.AllowReading();

        return(ret);
    }
Exemplo n.º 8
0
        public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName)
        {
            NodeTypeIndex typeIndex = (NodeTypeIndex)frameIndex;

            // Orphan node support
            if (typeIndex == m_graph.NodeTypeIndexLimit)
            {
                return("[not reachable from roots]");
            }

            var type       = m_graph.GetType(typeIndex, m_typeStorage);
            var moduleName = type.ModuleName;

            var ret = type.Name;

            if (moduleName != null)
            {
                if (verboseName)
                {
                    int length = moduleName.Length - 4;
                    if ((length >= 0) && (moduleName[length] == '.'))
                    {
                        moduleName = moduleName.Substring(0, length);
                    }
                }
                else
                {
                    moduleName = System.IO.Path.GetFileNameWithoutExtension(moduleName);
                }

                if (moduleName.Length == 0)
                {
                    moduleName = "?";
                }

                ret = moduleName + "!" + ShortenNameSpaces(type);
            }
            // TODO FIX NOW remove priority
            // ret +=  " " + m_typePriorities[(int)type.Index].ToString("f1");
            // TODO FIX NOW hack for CLRProfiler comparison
            // ret = Regex.Replace(ret, @" *\[\]", "[]");
            // ret = Regex.Replace(ret, @"`\d+", "");
            return(ret);
        }
Exemplo n.º 9
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;
            }
        }
    }
Exemplo n.º 10
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();
    }
Exemplo n.º 11
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);
    }
Exemplo n.º 12
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();
        }