Esempio n. 1
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);
    }
Esempio n. 2
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;
            }
        }
    }
Esempio n. 3
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();
        }
        public void AddSample(LinuxPerfScriptStackSourceSample sample)
        {
            var baseSample = AddSample((StackSourceSample)sample);

            m_LinuxPerfScriptSamples.Add(new LinuxPerfScriptStackSourceSample(baseSample, sample.CpuNumber));
        }
    /// <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;
    }
Esempio n. 6
0
        private void Read(Stream rawStream)
        {
            XmlReaderSettings settings = new XmlReaderSettings()
            {
                IgnoreWhitespace = true, IgnoreComments = true
            };
            XmlReader reader           = XmlTextReader.Create(rawStream, settings);
            var       stack            = new GrowableArray <StackSourceSample>();
            bool      metricsInclusive = false; // If true, we need to convert them to exclusive as part of processing

            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.Name == "node")
                    {
                        var    sample   = new StackSourceSample(this);
                        string callTree = reader.GetAttribute("call_tree");

                        // Case for allocation stacks
                        string sizeStr = reader.GetAttribute("size");
                        if (sizeStr != null)
                        {
                            metricsInclusive = true;        // allocation numbers are inclusive
                            int size = 0;
                            int.TryParse(sizeStr, out size);
                            sample.Metric = size;

                            string recoredObectsStr = reader.GetAttribute("recorded_objects");
                            int    recoredObects    = 0;
                            if (recoredObectsStr != null)
                            {
                                int.TryParse(recoredObectsStr, out recoredObects);
                            }

                            sample.Count = recoredObects;
                        }
                        else
                        {
                            Debug.Assert(metricsInclusive == false);        // CPU time is exclusive.
                            // For CPU
                            string own_time_msStr = reader.GetAttribute("own_time_ms");
                            if (own_time_msStr != null)
                            {
                                int own_time_ms;
                                int.TryParse(own_time_msStr, out own_time_ms);
                                sample.Metric = own_time_ms;

                                string countStr = reader.GetAttribute("count");
                                int    count    = 0;
                                if (countStr != null)
                                {
                                    int.TryParse(countStr, out count);
                                }

                                sample.Count = count;
                            }
                        }

                        // Get the parent stack for this line
                        var parentStackIndex = StackSourceCallStackIndex.Invalid;
                        int depth            = stack.Count;
                        if (depth > 0)
                        {
                            StackSourceSample parent = stack[depth - 1];
                            parentStackIndex = parent.StackIndex;

                            if (metricsInclusive)
                            {
                                // The values are inclusive, but StackSoruceSamples are the exclusive amounts, so remove children.
                                parent.Count  -= sample.Count;
                                parent.Metric -= sample.Metric;
                            }
                        }
                        if (callTree != null)
                        {
                            var frameIndex = Interner.FrameIntern(callTree);
                            sample.StackIndex = Interner.CallStackIntern(frameIndex, parentStackIndex);
                        }
                        stack.Add(sample);
                    }
                }
                if (reader.NodeType == XmlNodeType.EndElement || reader.IsEmptyElement)
                {
                    if (reader.Name == "node")
                    {
                        StackSourceSample sample = stack.Pop();
                        if ((sample.Count > 0 || sample.Metric > 0) && sample.StackIndex != StackSourceCallStackIndex.Invalid)
                        {
                            AddSample(sample);
                        }
                    }
                }
            }

            Debug.Assert(stack.Count == 0);
            Interner.DoneInterning();
        }
Esempio n. 7
0
        void Read(TextReader reader)
        {
            // TODO this is relatively inefficient.
            var regEx = new Regex(@"^\s*(\d+)\s*(\d+)\s*\[\s*(\d+)\s*\]\s*(\S*?)!?(.*)");

            var         stack = new GrowableArray <WTStackElem>();
            WTStackElem elem  = new WTStackElem();
            long        time  = 0;

            for (; ;)
            {
                var line = reader.ReadLine();
                if (line == null)
                {
                    break;
                }
                var match = regEx.Match(line);
                if (match.Success)
                {
                    int    excInstrSoFar = int.Parse(match.Groups[1].Value);
                    int    depth         = int.Parse(match.Groups[3].Value);
                    string module        = match.Groups[4].Value;
                    string method        = match.Groups[5].Value;

                    var moduleIndex = ModuleIntern(module);
                    var frameIndex  = FrameIntern(method, moduleIndex);

                    var parent = StackSourceCallStackIndex.Invalid;
                    if (depth > 0)
                    {
                        parent = stack[depth - 1].FirstCallStackIndex;    // TODO handle out of range
                    }
                    var callStackIndex = CallStackIntern(frameIndex, parent);

                    int extra = stack.Count - depth;
                    int exclInstr;
                    if (extra > 0)
                    {
                        elem = stack[depth];

                        if (callStackIndex == elem.CallStackIndex)
                        {
                            exclInstr = excInstrSoFar - elem.ExclInstrSoFar;
                        }
                        else
                        {
                            exclInstr           = excInstrSoFar;
                            elem.CallStackIndex = callStackIndex;
                        }
                        Debug.Assert(exclInstr >= 0);
                        stack.RemoveRange(depth, extra);
                    }
                    else
                    {
                        elem.CallStackIndex      = callStackIndex;
                        elem.FirstCallStackIndex = callStackIndex;
                        Debug.Assert(extra == 0);
                        exclInstr = excInstrSoFar;
                    }
                    elem.ExclInstrSoFar = excInstrSoFar;
                    stack.Add(elem);

                    time += exclInstr;

                    var sample = new StackSourceSample(this);
                    sample.SampleIndex = (StackSourceSampleIndex)m_samples.Count;
                    sample.Metric      = exclInstr;
                    sample.TimeRelMSec = time - exclInstr;
                    sample.StackIndex  = elem.FirstCallStackIndex;

                    // Break long sequences of instructions into individual samples.   This
                    // makes timeline work well.
                    // TODO this bloats the data, not clear if this is the right tradeoff ....
                    const int maxSize = 20;
                    while (sample.Metric > maxSize)
                    {
                        var subSample = new StackSourceSample(sample);
                        subSample.Metric    = maxSize;
                        sample.Metric      -= maxSize;
                        sample.TimeRelMSec += maxSize;
                        m_samples.Add(subSample);
                        sample.SampleIndex = (StackSourceSampleIndex)m_samples.Count;
                    }

                    m_samples.Add(sample);
#if DEBUG
                    var sampleStr = this.ToString(sample);
                    Debug.WriteLine(sampleStr);
#endif
                }
            }
            m_sampleTimeRelMSecLimit = time;
            CompletedReading();
        }
Esempio n. 8
0
        void Read(TextReader reader)
        {
            // TODO this is relatively inefficient.
            var regEx = new Regex(@"^\s*(\d+)\s*(\d+)\s*\[\s*(\d+)\s*\]\s*(\S*?)!?(.*)");

            var         stack  = new GrowableArray <WTStackElem>();
            WTStackElem elem   = new WTStackElem();
            long        time   = 0;
            var         sample = new StackSourceSample(this);

            for (; ;)
            {
                var line = reader.ReadLine();
                if (line == null)
                {
                    break;
                }
                var match = regEx.Match(line);
                if (match.Success)
                {
                    // Parse the line.
                    int    excInstrSoFar = int.Parse(match.Groups[1].Value);
                    int    depth         = int.Parse(match.Groups[3].Value);
                    string module        = match.Groups[4].Value;
                    string method        = match.Groups[5].Value;

                    // Form the name for this line
                    var moduleIndex = Interner.ModuleIntern(module);
                    var frameIndex  = Interner.FrameIntern(method, moduleIndex);

                    // Get the parent stack for this line
                    var parent = StackSourceCallStackIndex.Invalid;
                    if (depth > 0)
                    {
                        parent = stack[depth - 1].FirstCallStackIndex;    // TODO handle out of range
                    }
                    // Form the stack for this entry
                    var callStackIndex = Interner.CallStackIntern(frameIndex, parent);

                    int exclInstr;                              // Number of instructions executed on this line
                    int extra = stack.Count - depth;            // The number of frames we need to pop off (including me)
                    if (extra > 0)
                    {
                        // We returned from one or more methods OR we have not left the current method
                        //
                        elem = stack[depth];

                        // We expect to return to the same method we were at at this depth.
                        if (callStackIndex == elem.CallStackIndex)
                        {
                            exclInstr = excInstrSoFar - elem.ExclInstrSoFar;    // We are continuing the function
                        }
                        else
                        {
                            // We are tail-calling to another routine.
                            exclInstr           = excInstrSoFar;
                            elem.CallStackIndex = callStackIndex;
                        }

                        // Pop off all the frames we returned from
                        Debug.Assert(exclInstr >= 0);
                        stack.RemoveRange(depth, extra);
                    }
                    else
                    {
                        // Means we are adding a new frame (we called someone)
                        Debug.Assert(extra == 0);       // We always add only one more frame (e.g. we never go from depth 2 to 4)
                        elem.CallStackIndex      = callStackIndex;
                        elem.FirstCallStackIndex = callStackIndex;
                        exclInstr = excInstrSoFar;
                    }
                    elem.ExclInstrSoFar = excInstrSoFar;
                    stack.Add(elem);

                    time += exclInstr;

                    sample.Metric           = exclInstr;
                    sample.TimeRelativeMSec = time - exclInstr;
                    sample.StackIndex       = elem.FirstCallStackIndex;
                    AddSample(sample);
                }
            }
            Interner.DoneInterning();
        }
Esempio n. 9
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();
        }
Esempio n. 10
0
        void Read(TextReader reader)
        {
            var stack = new GrowableArray <StackSourceCallStackIndex>();


            var line   = reader.ReadLine(); // Skip the first line, which is column headers.
            var sample = new StackSourceSample(this);

            for (; ;)
            {
                line = reader.ReadLine();
                if (line == null)
                {
                    break;
                }
                //   0       1           2             3          4       5        6         7          8
                // Order, # of Calls, % Incl Time, % Excl Time, Depth, Function, Module, Incl Time, Excl Time,% Sw. Out, Incl Switched Out, Type, Comments	Min	Avg	Max	Excl Switched Out

                int    idx    = 0;
                int    depth  = 0;
                string method = null;
                string module = null;
                int    intVal;
                long   longVal;
                for (int col = 0; col <= 8; col++)
                {
                    var newIdx = line.IndexOf('\t', idx);
                    Debug.Assert(0 < newIdx);
                    if (newIdx < 0)
                    {
                        goto SKIP;
                    }

                    switch (col)
                    {
                    case 1:
                        int.TryParse(line.Substring(idx, newIdx - idx), System.Globalization.NumberStyles.Number, null, out intVal);
                        sample.Count = intVal;
                        break;

                    case 4:
                        int.TryParse(line.Substring(idx, newIdx - idx), System.Globalization.NumberStyles.Number, null, out depth);
                        break;

                    case 5:
                        while (idx < newIdx)
                        {
                            if (line[idx] != ' ')
                            {
                                break;
                            }
                            idx++;
                        }
                        method = line.Substring(idx, newIdx - idx);
                        method = method.Replace((char)0xFFFD, '@');          // They used this character to separate the method name from signature.
                        break;

                    case 6:
                        module = "";
                        if (depth != 0)
                        {
                            module = line.Substring(idx, newIdx - idx);
                        }
                        break;

                    case 8:
                        long.TryParse(line.Substring(idx, newIdx - idx), System.Globalization.NumberStyles.Number, null, out longVal);
                        sample.Metric = longVal / 1000000;     // TODO what is the metric?
                        break;
                    }
                    idx = newIdx + 1;
                }
                var moduleIdx = Interner.ModuleIntern(module);
                var frameIdx  = Interner.FrameIntern(method, moduleIdx);
                var prevFrame = StackSourceCallStackIndex.Invalid;
                if (0 < depth && depth <= stack.Count)
                {
                    prevFrame = stack[depth - 1];
                }
                var callStackIdx = Interner.CallStackIntern(frameIdx, prevFrame);

                if (depth < stack.Count)
                {
                    stack.Count = depth;
                }
                stack.Add(callStackIdx);

                sample.StackIndex = callStackIdx;
                AddSample(sample);
                SKIP :;
            }
            Interner.DoneInterning();
        }