예제 #1
0
        private static bool AddStatementToAggregatedCallTree(
            ProfilerEvent?currentProfilerEvent,
            ref AggregatedEventNode currentAggregatedEventNode,
            ref Stack <ProfilerEvent> callStack)
        {
            if (currentAggregatedEventNode.Parent == null)
            {
                // Do not add statements to root node
                return(true);
            }

            // If there are two consecutive statements then first we need to calculate the duration for the previous
            // one and pop it from the statement call stack.
            // Then we push the current statement event into the stack
            if (currentAggregatedEventNode.Parent != null && currentAggregatedEventNode.EvaluatedType == EventType.Statement ||
                // Function or sql statement called for previous statement has just ended
                currentAggregatedEventNode.EvaluatedType == EventType.StopMethod &&
                !currentAggregatedEventNode.IsExecutingFunction)
            {
                currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(currentProfilerEvent.Value.TimeStampRelativeMSec);
            }

            currentAggregatedEventNode.EvaluatedType = EventType.Statement;

            currentAggregatedEventNode = currentAggregatedEventNode.PushEventIntoCallStack(currentProfilerEvent.Value);

            return(true);
        }
예제 #2
0
        private static void PopEventFromCallStackForPreviousAlStopMethodEvent(
            ProfilerEvent?previousProfilerEvent,
            ProfilerEvent?currentProfilerEvent,
            ref AggregatedEventNode currentAggregatedEventNode)
        {
            if (previousProfilerEvent.HasValue && previousProfilerEvent.Value.Type == EventType.StopMethod &&
                previousProfilerEvent.Value.SubType != EventSubType.SqlEvent)
            {
                // We have already calculated duration for the previous statement
                // and pop it from the statement call stack.

                // We should never pop root event. This can happen if we miss some events in the begining.
                if (currentAggregatedEventNode.Parent != null)
                {
                    if (!currentProfilerEvent.HasValue || // Previous event is the last one.
                        // The current event is none AL event. It comes after stop event. Need to pop the the current aggregated node. Only close AL events.
                        (currentProfilerEvent.Value.IsNonAlEvent && previousProfilerEvent.Value.IsAlEvent) ||
                        // Here we have statement after function call.
                        currentProfilerEvent.Value.Type == EventType.Statement ||
                        // Here we have funtion call in the end of other function.
                        currentProfilerEvent.Value.Type == EventType.StopMethod ||
                        // If we have two consecutive root method calls
                        (currentAggregatedEventNode.OriginalType == EventType.StartMethod &&
                         currentAggregatedEventNode.IsAlEvent))
                    {
                        currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(previousProfilerEvent.Value.TimeStampRelativeMSec);
                    }
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Processes aggregated event. This method calls either <see cref="AggregatedEventNode.PushEventIntoCallStack"/> or
        /// <see cref="AggregatedEventNode.PopEventFromCallStackAndCalculateDuration"/> on the <see cref="currentAggregatedEventNode"/>.
        /// </summary>
        /// <param name="previousProfilerEvent">Previous profiler event which was processed.</param>
        /// <param name="currentProfilerEvent">Profiler event which currently being processed.</param>
        /// <param name="currentAggregatedEventNode">Current aggregated event in the call tree.</param>
        /// <returns>Returns <c>true</c> if event was valid and should not be skipped.</returns>
        internal static bool AddProfilerEventToAggregatedCallTree(
            ProfilerEvent?previousProfilerEvent,
            ProfilerEvent?currentProfilerEvent,
            ref AggregatedEventNode currentAggregatedEventNode,
            ref Stack <ProfilerEvent> callStack)
        {
            bool skipCurrentEvent;

            /*if (PopEventFromCallStackIfSomeEventsWereMissing(previousProfilerEvent, currentProfilerEvent, ref currentAggregatedEventNode, ref callStack, out skipCurrentEvent))
             * {
             *  return skipCurrentEvent;
             * }
             *
             * PopEventFromCallStackForPreviousAlStopMethodEvent(previousProfilerEvent, currentProfilerEvent, ref currentAggregatedEventNode, ref callStack);*/

            if (!currentProfilerEvent.HasValue)
            {
                return(true);
            }

            switch (currentProfilerEvent.Value.Type)
            {
            case EventType.Statement:
                return(AddStatementToAggregatedCallTree(currentProfilerEvent, ref currentAggregatedEventNode, ref callStack));

            case EventType.StartMethod:
                return(AddStartMethodToAggregatedCallTree(previousProfilerEvent, currentProfilerEvent, ref currentAggregatedEventNode, ref callStack));

            case EventType.StopMethod:
                return(AddStopMethodToAggregatedCallTree(currentProfilerEvent, ref currentAggregatedEventNode, ref callStack));
            }

            return(true);
        }
예제 #4
0
        private static bool AddStopMethodToAggregatedCallTree(
            ProfilerEvent?currentProfilerEvent,
            ref AggregatedEventNode currentAggregatedEventNode,
            ref Stack <ProfilerEvent> callStack)
        {
            if (currentAggregatedEventNode.Parent != null)
            {
                if (callStack.Count() == 0)
                {
                    // Discard current tree
                    while (currentAggregatedEventNode.Parent != null)
                    {
                        currentAggregatedEventNode = currentAggregatedEventNode.Parent;
                    }

                    currentAggregatedEventNode.Children.Clear();

                    return(false);
                }

                ProfilerEvent startEvent = callStack.Peek();
                if (currentProfilerEvent.Value.ObjectType != startEvent.ObjectType ||
                    currentProfilerEvent.Value.ObjectId != startEvent.ObjectId ||
                    currentProfilerEvent.Value.StatementName != startEvent.StatementName)
                {
                    // Skip stop event from empty functions
                    return(false);
                }

                // We need to calculate duration for the previous statement and pop it from the statement call stack.
                if (currentAggregatedEventNode.EvaluatedType == EventType.Statement ||
                    currentAggregatedEventNode.EvaluatedType == EventType.StopMethod ||
                    // Always close non events.
                    currentProfilerEvent.Value.IsNonAlEvent)
                {
                    currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(currentProfilerEvent.Value.TimeStampRelativeMSec);

                    if (currentProfilerEvent.Value.SubType == EventSubType.AlEvent &&
                        currentAggregatedEventNode.SubType == EventSubType.AlEvent &&
                        currentAggregatedEventNode.Parent != null &&
                        currentAggregatedEventNode.Parent.Parent == null)
                    {
                        // Also pop function node created for root functions
                        currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(currentProfilerEvent.Value.TimeStampRelativeMSec);
                    }
                }

                currentAggregatedEventNode.EvaluatedType = EventType.StopMethod;

                if (currentProfilerEvent.Value.IsAlEvent)
                {
                    currentAggregatedEventNode.IsExecutingFunction = false;
                }

                callStack.Pop();
            }

            return(true);
        }
예제 #5
0
        /// <summary>
        /// Traverses the call stack tree.
        /// </summary>
        /// <param name="aggregatedCallTree">Aggregated call tree to traverse.</param>
        /// <returns>Flatten call tree.</returns>
        private static IEnumerable <AggregatedEventNode> FlattenCallTree(AggregatedEventNode aggregatedCallTree)
        {
            yield return(aggregatedCallTree);

            foreach (AggregatedEventNode node in aggregatedCallTree.Children)
            {
                foreach (AggregatedEventNode childNode in FlattenCallTree(node))
                {
                    yield return(childNode);
                }
            }
        }
예제 #6
0
        private static void PopEventFromCallStackForPreviousAlStopMethodEvent(
            ProfilerEvent?previousProfilerEvent,
            ProfilerEvent?currentProfilerEvent,
            ref AggregatedEventNode currentAggregatedEventNode,
            ref Stack <ProfilerEvent> callStack)
        {
            if (previousProfilerEvent.HasValue && previousProfilerEvent.Value.Type == EventType.StopMethod &&
                previousProfilerEvent.Value.SubType != EventSubType.SqlEvent)
            {
                // We have already calculated duration for the previous statement
                // and pop it from the statement call stack.

                // We should never pop root event. This can happen if we miss some events in the begining.
                if (currentAggregatedEventNode.Parent != null)
                {
                    if (!currentProfilerEvent.HasValue || // Previous event is the last one.
                                                          // The current event is none AL event. It comes after stop event. Need to pop the the current aggregated node. Only close AL events.
                        (currentProfilerEvent.Value.IsNonAlEvent && previousProfilerEvent.Value.IsAlEvent) ||
                        // Here we have statement after function call.
                        currentProfilerEvent.Value.Type == EventType.Statement ||
                        // Here we have funtion call in the end of other function.
                        currentProfilerEvent.Value.Type == EventType.StopMethod ||
                        // If we have two consecutive root method calls
                        (currentAggregatedEventNode.OriginalType == EventType.StartMethod &&
                         currentAggregatedEventNode.IsAlEvent))
                    {
                        if (currentProfilerEvent.HasValue && currentProfilerEvent.Value.IsNonAlEvent && previousProfilerEvent.Value.IsAlEvent)
                        {
                            return;
                        }

                        if (currentProfilerEvent.HasValue && currentProfilerEvent.Value.Type == EventType.StopMethod && currentProfilerEvent.Value.SubType == EventSubType.AlEvent && (
                                callStack.Count() == 0 ||
                                callStack.Peek().ObjectType != currentProfilerEvent.Value.ObjectType ||
                                callStack.Peek().ObjectId != currentProfilerEvent.Value.ObjectId ||
                                callStack.Peek().FunctionName != currentProfilerEvent.Value.StatementName))
                        {
                            return;
                        }

                        //AggregatedEventNode currentAggregatedEventNode2 = currentAggregatedEventNode;
                        //List<AggregatedEventNode> before = currentAggregatedEventNode.Parent.Children.TakeWhile(x => x != currentAggregatedEventNode2).ToList();

                        //if (currentAggregatedEventNode.OriginalType == EventType.StartMethod && currentAggregatedEventNode.IsAlEvent)
                        //    if (before.Any(x => x.EvaluatedType == EventType.StartMethod) && !before.Any(x => x.OriginalType == EventType.Statement))
                        //        return;

                        currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(previousProfilerEvent.Value.TimeStampRelativeMSec);
                    }
                }
            }
        }
예제 #7
0
        /// <summary>
        /// Initializes state of the <see cref="DynamicProfilerEventProcessor"/>
        /// </summary>
        public void Initialize()
        {
            // create the root node for the session
            aggregatedCallTree = new AggregatedEventNode
            {
                StatementName = "Session: " + profilingSessionId + ";",
                SessionId     = profilingSessionId
            };

            currentAggregatedEventNode = aggregatedCallTree;

            previousProfilerEvent = null;
        }
예제 #8
0
        private static bool AddStartMethodToAggregatedCallTree(ProfilerEvent?previousProfilerEvent, ProfilerEvent?currentProfilerEvent, ref AggregatedEventNode currentAggregatedEventNode)
        {
            // If it is the root method or if it is non AL event we also push start event into the stack.
            if (currentAggregatedEventNode.Parent == null ||
                currentProfilerEvent.Value.IsNonAlEvent ||
                (previousProfilerEvent.HasValue && previousProfilerEvent.Value.IsNonAlEvent))
            {
                currentAggregatedEventNode = currentAggregatedEventNode.PushEventIntoCallStack(currentProfilerEvent.Value);
            }

            currentAggregatedEventNode.EvaluatedType = EventType.StartMethod;

            return(true);
        }
예제 #9
0
        private static bool AddStatementToAggregatedCallTree(ProfilerEvent?currentProfilerEvent, ref AggregatedEventNode currentAggregatedEventNode)
        {
            // If there are two consecutive statements then first we need to calculate the duration for the previous
            // one and pop it from the statement call stack.
            // Then we push the current statement event into the stack
            if (currentAggregatedEventNode.Parent != null && currentAggregatedEventNode.EvaluatedType == EventType.Statement)
            {
                currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(currentProfilerEvent.Value.TimeStampRelativeMSec);
            }

            currentAggregatedEventNode = currentAggregatedEventNode.PushEventIntoCallStack(currentProfilerEvent.Value);

            return(true);
        }
예제 #10
0
        /// <summary>
        /// Initializes state of the <see cref="DynamicProfilerEventProcessor"/>
        /// </summary>
        public void Initialize()
        {
            // create the root node for the session
            this.aggregatedCallTree = new AggregatedEventNode
            {
                StatementName = "Session: " + this.profilingSessionId + ";",
                SessionId     = this.profilingSessionId
            };

            this.currentAggregatedEventNode = aggregatedCallTree;

            this.callStack = new Stack <ProfilerEvent>();

            this.previousProfilerEvent = null;
        }
예제 #11
0
        private static bool AddStopMethodToAggregatedCallTree(ProfilerEvent?currentProfilerEvent, ref AggregatedEventNode currentAggregatedEventNode)
        {
            if (currentAggregatedEventNode.Parent != null)
            {
                // We need to calculate duration for the previous statement and pop it from the statement call stack.
                if (currentAggregatedEventNode.EvaluatedType == EventType.Statement ||
                    // Always close non events.
                    currentProfilerEvent.Value.IsNonAlEvent)
                {
                    currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(currentProfilerEvent.Value.TimeStampRelativeMSec);
                }
            }

            return(true);
        }
        /// <summary>
        /// Pushes event into call stack. It might get or create aggregated event.
        /// </summary>
        /// <param name="profilerEvent">The profiler event.</param>
        /// <returns>The aggregated event node.</returns>
        internal AggregatedEventNode PushEventIntoCallStack(ProfilerEvent profilerEvent)
        {
            Debug.Assert(profilerEvent.Type == EventType.Statement || profilerEvent.Type == EventType.StartMethod);

            AggregatedEventNode res = Children.Find(e =>
                                                    e.SessionId == profilerEvent.SessionId &&
                                                    e.ObjectType == profilerEvent.ObjectType &&
                                                    e.ObjectId == profilerEvent.ObjectId &&
                                                    e.LineNo == profilerEvent.LineNumber &&
                                                    e.StatementName == profilerEvent.StatementName);

            if (res != null)
            {
                // We need to initialize state of the AggregatedEventNode.
                // Otherwise duration will not be calculated correctly or we can get broken tree structure.
                res.EvaluatedType         = profilerEvent.Type;
                res.TimeStampRelativeMSec = profilerEvent.TimeStampRelativeMSec;

                ++res.HitCount;
                return(res);
            }

            res = new AggregatedEventNode(this)
            {
                SessionId             = profilerEvent.SessionId,
                Tenant                = profilerEvent.Tenant,
                UserName              = profilerEvent.UserName,
                AppId                 = profilerEvent.AppId,
                AppInfo               = profilerEvent.AppInfo,
                ObjectType            = profilerEvent.ObjectType,
                ObjectId              = profilerEvent.ObjectId,
                LineNo                = profilerEvent.LineNumber,
                StatementName         = profilerEvent.StatementName,
                TimeStampRelativeMSec = profilerEvent.TimeStampRelativeMSec,
                OriginalType          = profilerEvent.Type,
                EvaluatedType         = profilerEvent.Type,
                SubType               = profilerEvent.SubType,
            };

            Children.Add(res);

            ++res.HitCount;
            return(res);
        }
예제 #13
0
        /// <summary>
        /// Traverses the call stack tree.
        /// </summary>
        /// <returns>reduces the tree by removing all nodes that are below the threshold call tree.</returns>
        private void ReduceTree(AggregatedEventNode rootNode)
        {
            if (this.threshold <= 0)
            {
                return;
            }

            for (int index = 0; index < rootNode.Children.Count; index++)
            {
                AggregatedEventNode node = rootNode.Children[index];
                if (node.DurationMSec < this.threshold)
                {
                    node.Children.Clear();
                    rootNode.Children.Remove(node);
                    index--;
                }
                else
                {
                    this.ReduceTree(node);
                }
            }
        }
예제 #14
0
        private static bool AddStartMethodToAggregatedCallTree(
            ProfilerEvent?previousProfilerEvent,
            ProfilerEvent?currentProfilerEvent,
            ref AggregatedEventNode currentAggregatedEventNode,
            ref Stack <ProfilerEvent> callStack)
        {
            // If it is the root method or if it is non AL event we also push start event into the stack.
            if (currentAggregatedEventNode.Parent == null ||
                currentProfilerEvent.Value.IsNonAlEvent)
            {
                currentAggregatedEventNode = currentAggregatedEventNode.PushEventIntoCallStack(currentProfilerEvent.Value);
            }

            currentAggregatedEventNode.EvaluatedType = EventType.StartMethod;

            if (currentProfilerEvent.Value.IsAlEvent)
            {
                currentAggregatedEventNode.IsExecutingFunction = true;
            }

            callStack.Push(currentProfilerEvent.Value);

            return(true);
        }
예제 #15
0
 /// <summary>
 /// Initializes a new instance of the <see cref="AggregatedEventNode"/> class.
 /// </summary>
 /// <param name="parent">Parent <see cref="AggregatedEventNode"/>.</param>
 internal AggregatedEventNode(AggregatedEventNode parent = null)
 {
     this.Children = new List <AggregatedEventNode>();
     this.Parent   = parent;
     this.Depth    = parent != null ? parent.Depth + 1 : 0;
 }
예제 #16
0
        private static bool PopEventFromCallStackIfSomeEventsWereMissing(
            ProfilerEvent?previousProfilerEvent,
            ProfilerEvent?currentProfilerEvent,
            ref AggregatedEventNode currentAggregatedEventNode,
            ref Stack <ProfilerEvent> callStack,
            out bool skipCurrentEvent)
        {
            if (currentAggregatedEventNode.Parent != null &&
                currentAggregatedEventNode.OriginalType == EventType.StartMethod &&
                currentAggregatedEventNode.SubType == EventSubType.SqlEvent &&
                currentProfilerEvent.HasValue && !(currentProfilerEvent.Value.Type == EventType.StopMethod && currentProfilerEvent.Value.SubType == EventSubType.SqlEvent))
            {
                // TODO: Here we have two consecutive start events. First event is of the SQL subtype. This should never happen because SQL queries cannot be nested.
                // TODO: It could indicates an issue in evening.
                // TODO: Need to pop previous event and push current.

                ProfilerEvent missingProfilerEvent = new ProfilerEvent
                {
                    SessionId     = currentAggregatedEventNode.SessionId,
                    ObjectType    = currentAggregatedEventNode.ObjectType,
                    ObjectId      = currentAggregatedEventNode.ObjectId,
                    LineNo        = currentAggregatedEventNode.LineNo,
                    Type          = EventType.StartMethod,
                    StatementName = StopEventIsMissing + currentAggregatedEventNode.StatementName
                };

                currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(previousProfilerEvent.Value.TimeStampRelativeMSec);

                currentAggregatedEventNode = currentAggregatedEventNode.PushEventIntoCallStack(missingProfilerEvent);
                currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(missingProfilerEvent.TimeStampRelativeMSec);

                if (currentProfilerEvent.Value.Type == EventType.StartMethod || currentProfilerEvent.Value.Type == EventType.Statement)
                {
                    currentAggregatedEventNode = currentAggregatedEventNode.PushEventIntoCallStack(currentProfilerEvent.Value);
                }

                skipCurrentEvent = true;
                return(true);
            }

            if (currentAggregatedEventNode.Parent != null &&
                previousProfilerEvent.HasValue && previousProfilerEvent.Value.Type == EventType.StopMethod &&
                currentProfilerEvent.HasValue && currentProfilerEvent.Value.Type == EventType.StopMethod &&
                currentProfilerEvent.Value.IsAlEvent != currentAggregatedEventNode.IsAlEvent)
            {
                //TODO: We hit this block for example in the case if Codeunit 1 trigger is called and it does not have any code.
                //TODO: We should consider if we want to fix it in the product.
                // Skip this event. Should never happen. Indicates a issue in the event generation.
                // Some events were missed.

                // Create fake start event.
                ProfilerEvent profilerEvent = currentProfilerEvent.Value;
                profilerEvent.Type          = EventType.StartMethod;
                profilerEvent.StatementName = StartEventIsMissing + profilerEvent.StatementName;

                currentAggregatedEventNode = currentAggregatedEventNode.PushEventIntoCallStack(profilerEvent);
                currentAggregatedEventNode = currentAggregatedEventNode.PopEventFromCallStackAndCalculateDuration(currentProfilerEvent.Value.TimeStampRelativeMSec);

                skipCurrentEvent = false;
                return(true);
            }

            skipCurrentEvent = false;
            return(false);
        }