示例#1
0
        /**
         * Recursively traverses tree updating the tree node's text with inclusive usec per number of toplevel calls.
         *
         * @param	Parent			Node to traverse.
         * @param	TopLevelCalls	Number of calls to top level node.
         */
        private void RecurseNodesExpensiveFunctions(TreeNode Parent, int TopLevelCalls)
        {
            foreach (TreeNode ChildNode in Parent.Nodes)
            {
                // Update text with inclusive usec per number of toplevel function calls.
                NodePayload  Payload     = (NodePayload)ChildNode.Tag;
                FunctionInfo Function    = (FunctionInfo)PointerToFunctionInfo[Payload.FunctionPointer];
                String       usecPerCall = String.Format("{0:0.00}", usecsPerCycle * Payload.Cycles / TopLevelCalls).PadLeft(9, ' ');
                ChildNode.Text = usecPerCall + " usec  " + Function.Name;

                // And recurse.
                RecurseNodesExpensiveFunctions(ChildNode, TopLevelCalls);
            }
        }
示例#2
0
        /**
         * Parses token stream and updates the passed in treeviews and listview.
         *
         * File format:
         *
         * DOUBLE				usecsPerCycle
         * DWORD				NumFunctions
         *		DWORD			Function pointer
         *		DWORD			Length of string
         *		ANSICHAR[Lengt]	Function name
         *
         * DWORD[...]			Tokens
         *
         * @param	Filename			Filename to open
         * @param	CallGraph			TreeView used to parse call graph information sorted by %inclusive time into
         * @param	ExpensiveFunctions	TreeView used to parse call graph information sorted by absolute inclusive time per parent call into
         * @param	SortedView			ListView used to parse various stats into
         * @param	LoadingProgress		Progress bar that is being updated as we parse
         */
        public void ParseStream(String Filename, TreeView CallGraph, TreeView ExpensiveFunctions, ListView SortedView, ProgressBar LoadingProgress)
        {
            // Empty views as we might be opening a stream for the second time.
            CallGraph.Nodes.Clear();
            ExpensiveFunctions.Nodes.Clear();
            SortedView.Items.Clear();

            // Create binary reader and file info object from filename.
            BinaryReader BinaryStream = new BinaryReader(File.OpenRead(Filename));
            FileInfo     Info         = new FileInfo(Filename);
            long         BytesRead    = 0;

            // Begin parsing the stream.
            usecsPerCycle = BinaryStream.ReadDouble();
            UInt32 NumFunctions = BinaryStream.ReadUInt32();

            BytesRead            += 4;
            PointerToFunctionInfo = new Hashtable();

            // Populate the function pointer to name map with data serialized from stream.
            for (uint FunctionIndex = 0; FunctionIndex < NumFunctions; FunctionIndex++)
            {
                UInt32 Pointer      = BinaryStream.ReadUInt32();
                UInt32 Length       = BinaryStream.ReadUInt32();
                String FunctionName = new String(BinaryStream.ReadChars((int)Length));
                PointerToFunctionInfo.Add(Pointer, new FunctionInfo(FunctionName));

                BytesRead            += 4 + 4 + (long)Length;
                LoadingProgress.Value = (int)(80 * BytesRead / Info.Length);
            }

            // Create "self" pointer function info required for name lookup later.
            PointerToFunctionInfo.Add(SelfPointer, new FunctionInfo("self"));

            // Create dummy root node which is going to be removed later.
            TreeNode CurrentNode = new TreeNode("Call Graph");
            TreeNode TopNode     = CurrentNode;
            bool     Finished    = false;
            Stack    CallStack   = new Stack();

            CallGraph.Nodes.Add(CurrentNode);

            // Parse token stream.
            while (!Finished)
            {
                try
                {
                    // Read token from stream and update progress bar.
                    RawToken Token = new RawToken(BinaryStream.ReadUInt32());
                    BytesRead            += 4;
                    LoadingProgress.Value = (int)(80 * BytesRead / Info.Length);

                    if (Token.IsFunction())
                    {
                        // Function call token.

                        bool        Found           = false;
                        UInt32      FunctionPointer = Token.GetValue();
                        NodePayload Payload         = (NodePayload)CurrentNode.Tag;

                        FunctionInfo Function = (FunctionInfo)PointerToFunctionInfo[FunctionPointer];
                        Function.Calls++;

                        if (Payload != null && Payload.FunctionPointer == FunctionPointer)
                        {
                            // Simple recursion.
                            // @warning: the code neither handles mutual recursion nor non trivial loops correctly.
                            Payload.Calls++;
                        }
                        else
                        {
                            // See whether we already created a node for this function...
                            foreach (TreeNode ChildNode in CurrentNode.Nodes)
                            {
                                NodePayload ChildPayload = (NodePayload)ChildNode.Tag;
                                if (ChildPayload.FunctionPointer == FunctionPointer)
                                {
                                    Found       = true;
                                    CurrentNode = ChildNode;
                                    break;
                                }
                            }

                            // ... and set up one if not.
                            if (!Found)
                            {
                                TreeNode ChildNode = new TreeNode("");
                                CurrentNode.Nodes.Add(ChildNode);
                                CurrentNode     = ChildNode;
                                CurrentNode.Tag = new NodePayload(CurrentNode, FunctionPointer);
                            }

                            // CurrentNode now points to "this" function call.

                            Payload = (NodePayload)CurrentNode.Tag;
                            Payload.Calls++;
                        }

                        // Keep track of call stack so we can detect and correctly handle simple recursion.
                        CallStack.Push(Function);
                    }
                    else if (Token.IsCycleCount())
                    {
                        // Cycle count token.

                        FunctionInfo Function = (FunctionInfo)CallStack.Pop();
                        if ((CallStack.Count > 0) && (CallStack.Peek() == Function))
                        {
                            // Simple recursion.
                            // @warning: the code neither handles mutual recursion nor non trivial loops correctly.
                        }
                        else
                        {
                            // Update inclusive time and "return" by walking up the tree.
                            NodePayload Payload = (NodePayload)CurrentNode.Tag;
                            CurrentNode     = CurrentNode.Parent;
                            Payload.Cycles += Token.GetValue();
                            if (CurrentNode.Parent == null)
                            {
                                // A "null" parent indicates we returned form a top level so we update the total
                                // cycle count.
                                TotalCycleCount += Token.GetValue();
                            }
                        }
                    }
                    else if (Token.IsFrameEndMarker())
                    {
                        // End of frame marker.

                        if (CallStack.Count != 0)
                        {
                            //@todo: better error handling.
                            Console.WriteLine("Stack underflow.");
                            Finished = true;
                        }
                        FrameCount++;
                    }
                    else if (Token.IsEndMarker())
                    {
                        // End of stream marker.

                        Console.WriteLine("Stack depth == {0}", CallStack.Count);
                        Finished = true;
                    }
                    else
                    {
                        //@todo: better error handling.
                        Console.WriteLine("Unrecognized token.");
                        Finished = true;
                    }
                }
                catch (System.IO.EndOfStreamException)
                {
                    //@todo: better error handling.
                    Console.WriteLine("Unexpected end of stream.");
                    Finished = true;
                }
            }

            LoadingProgress.Value = 80;
            // Recurse all nodes, update information, add "self" where necessary and sort nodes based on percentage. Yes, this function does a lot.
            RecurseNodes(TopNode);
            LoadingProgress.Value = 90;

            // Clone *entire* tree for "expensive function graph.
            TreeNode OtherTopNode = (TreeNode)TopNode.Clone();

            // Remove dummy top node from default call graph tree.
            CallGraph.Nodes.Clear();
            foreach (TreeNode Node in TopNode.Nodes)
            {
                CallGraph.Nodes.Add(Node);
            }

            // Keep track of top level functions in a sortable array.
            ArrayList ExpensiveFunctionsList = new ArrayList();

            foreach (TreeNode Node in OtherTopNode.Nodes)
            {
                ExpensiveFunctionsList.Add(Node);
            }

            // Display inclusive time in usec per call for top level functions...
            foreach (TreeNode Node in ExpensiveFunctionsList)
            {
                NodePayload  Payload     = (NodePayload)Node.Tag;
                FunctionInfo Function    = (FunctionInfo)PointerToFunctionInfo[Payload.FunctionPointer];
                String       usecPerCall = String.Format("{0:0.00}", usecsPerCycle * Payload.Cycles / Payload.Calls).PadLeft(9, ' ');
                Node.Text = usecPerCall + " usec  " + Function.Name;

                // ... and traverse the tree to update the displayed text with inclusive time in usec per top level function call.
                RecurseNodesExpensiveFunctions(Node, Payload.Calls);
            }

            // Sort expensive function tree view top nodes.
            NodeTextDescendingComparer Comparer = new NodeTextDescendingComparer();

            ExpensiveFunctionsList.Sort(Comparer);

            // Add nodes in sorted order. Being able to sort a NodeCollection directly would be nice.
            ExpensiveFunctions.Nodes.Clear();
            foreach (TreeNode Node in ExpensiveFunctionsList)
            {
                ExpensiveFunctions.Nodes.Add(Node);
            }

            // Populate list view with stats and sort it.
            foreach (FunctionInfo Function in PointerToFunctionInfo.Values)
            {
                // No need to pollute list with uncalled functions (comparatively large fraction).
                if (Function.Calls > 0)
                {
                    String   InclPercentage = String.Format("{0:0.00}", 100 * Function.InclCycles / TotalCycleCount).PadLeft(5, ' ');
                    String   ExclPercentage = String.Format("{0:0.00}", 100 * Function.ExclCycles / TotalCycleCount).PadLeft(5, ' ');
                    String   CallsPerFrame  = String.Format("{0:0.0000}", (float)Function.Calls / FrameCount).PadLeft(7, ' ');
                    String   InclPerCall    = String.Format("{0:0.00}", usecsPerCycle * Function.InclCycles / Function.Calls).PadLeft(9, ' ');
                    String   ExclPerCall    = String.Format("{0:0.00}", usecsPerCycle * Function.ExclCycles / Function.Calls).PadLeft(9, ' ');
                    string[] Row            = new string[] { Function.Name, InclPercentage, ExclPercentage, CallsPerFrame, InclPerCall, ExclPerCall };
                    SortedView.Items.Add(new ListViewItem(Row));
                }
            }
            SortedView.Sort();

            LoadingProgress.Value = 100;
        }
示例#3
0
        /**
         * Recursively traverses the tree bottom down, filling in the node text and adding self nodes where necessary
         *
         * @param	Parent	Parent tree node.
         */
        private void RecurseNodes(TreeNode Parent)
        {
            // Total amount of cycles spent in child nodes.
            double ChildCycles = 0;
            // List of nodes used for sorting as NodeCollection cannot be sorted :(
            ArrayList ChildNodeList = new ArrayList();

            // Traverse the tree recursively, set the text on nodes and collect total time spent in child nodes.
            foreach (TreeNode ChildNode in Parent.Nodes)
            {
                NodePayload Payload = (NodePayload)ChildNode.Tag;
                if (Payload != null)
                {
                    ChildCycles += Payload.Cycles;
                }

                RecurseNodes(ChildNode);

                FunctionInfo Function   = (FunctionInfo)PointerToFunctionInfo[Payload.FunctionPointer];
                String       Percentage = String.Format("{0:0.00}", 100 * Payload.Cycles / TotalCycleCount).PadLeft(5, ' ');
                ChildNode.Text = Percentage + "%  " + Function.Name;

                ChildNodeList.Add(ChildNode);
            }

            // Update incl./ excl. cycles and add a self node if wanted.
            NodePayload ParentPayload = (NodePayload)Parent.Tag;

            // Top node doesn't have payload.
            if (ParentPayload != null)
            {
                double       ExclCycles = ParentPayload.Cycles - ChildCycles;
                FunctionInfo Function   = (FunctionInfo)PointerToFunctionInfo[ParentPayload.FunctionPointer];
                Function.ExclCycles += ExclCycles;
                Function.InclCycles += ParentPayload.Cycles;

                // Add a "self" node to the parent for exclusive time unless we're a leaf.
                if (Parent.Nodes.Count > 0)
                {
                    TreeNode    SelfTimeNode = new TreeNode("");
                    NodePayload Payload      = new NodePayload(SelfTimeNode, SelfPointer);
                    SelfTimeNode.Tag = Payload;
                    Payload.Cycles   = ExclCycles;
                    FunctionInfo SelfFunction = (FunctionInfo)PointerToFunctionInfo[Payload.FunctionPointer];
                    String       Percentage   = String.Format("{0:0.00}", 100 * Payload.Cycles / TotalCycleCount).PadLeft(5, ' ');
                    SelfTimeNode.Text = Percentage + "%  " + SelfFunction.Name;
                    ChildNodeList.Add(SelfTimeNode);
                }
            }

            // Sort nodes in descending order.
            NodeTextDescendingComparer Comparer = new NodeTextDescendingComparer();

            ChildNodeList.Sort(Comparer);

            // Add nodes in sorted order. Being able to sort a NodeCollection directly would be nice.
            Parent.Nodes.Clear();
            foreach (TreeNode Node in ChildNodeList)
            {
                Parent.Nodes.Add(Node);
            }
        }