private void CalcThreadline(long currentTick) { // clear thread map so order of items is kept and we dont get threads switching around RemoveLines.Clear(); foreach (var timeline in Threadlines.Values) { timeline.Sequence.Clear(); timeline.DepthSet.Clear(); if (!timeline.IsAlive) { RemoveLines.Add(timeline.ThreadID); } } foreach (var id in RemoveLines) { Threadlines.Remove(id); } AddedNodes.Clear(); foreach (var flow in XRay.FlowMap) { if (ShowThreads != null && !ShowThreads.Contains(flow.ThreadID)) { continue; } if (ShowThreads == null && !flow.IsAlive) { continue; } long startTick = 0; int startDepth = XRay.MaxStack; foreach (var item in flow.EnumerateThreadline()) { if (startTick == 0) { startTick = item.StartTick; } // we should be reading into the past, if the next item is // in the future, then we are reading space that the app as logged // since this process was started (its nice cause we avoid locks on the timeline if (item.StartTick > startTick) { break; } if (AddToTimeline(flow, item)) { if (item.Depth < startDepth) { startDepth = item.Depth; } } } // add stack items that have been pushed off threadline (functions that haven't exited yet) for (int i = startDepth - 1; i >= 0; i--) { var item = flow.Stack[i]; if (item != null) { AddToTimeline(flow, item); } } } // remove empty threads var removeTimelines = Threadlines.Values.Where(t => t.Sequence.Count == 0).ToArray(); foreach (var timeline in removeTimelines) { Threadlines.Remove(timeline.ThreadID); } }
public void Render() { // clear and pre-process marked depencies RecalcDependencies(); // figure out if we need to do a search if (SearchString != LastSearch) { DoSearch(); } // draw layout ScreenSize.Width = Renderer.ViewWidth * ZoomFactor; ScreenSize.Height = Renderer.ViewHeight * ZoomFactor; ScreenOffset.X = PanOffset.X * ScreenSize.Width; // +(Width * CenterFactor.X - ModelSize.Width * CenterFactor.X); ScreenOffset.Y = PanOffset.Y * ScreenSize.Height; // +(Height * CenterFactor.Y - ModelSize.Height * CenterFactor.Y); if (ViewLayout == LayoutType.TreeMap) { DrawTreeMap(Renderer); if (ShowingOutside) { Renderer.DrawTextBackground(XColors.BorderColor, InternalRoot.AreaF.Width, 0, PanelBorderWidth, InternalRoot.AreaF.Height); DrawNode(InternalRoot, 0, true); } if (ShowingExternal) { Renderer.DrawTextBackground(XColors.BorderColor, ExternalRoot.AreaF.X - PanelBorderWidth, 0, PanelBorderWidth, ExternalRoot.AreaF.Height); DrawNode(ExternalRoot, 0, true); } DrawNode(CurrentRoot, 0, true); } else if (ViewLayout == LayoutType.CallGraph) { DrawCallGraph(); // id 0 nodes are intermediates foreach (var node in PositionMap.Values) { //foreach (var node in Graphs.SelectMany(g => g.Nodes()).Where(n => n.ID != 0)) DrawNode(node, 0, false); } } else if (ViewLayout == LayoutType.Timeline) { DrawTheadline(); foreach (var node in ThreadlineNodes) { DrawNode(node.Node, node.Area, node.LabelArea, 0, false, node.ShowHit); } } // draw ignored over nodes ignored may contain /*foreach (var ignored in IgnoredNodes.Values) * if (PositionMap.ContainsKey(ignored.ID)) * { * Renderer.DrawLine(XColors.IgnoredColor, 1, ignored.AreaF.UpperLeftCorner(), ignored.AreaF.LowerRightCorner(), false); * Renderer.DrawLine(XColors.IgnoredColor, 1, ignored.AreaF.UpperRightCorner(), ignored.AreaF.LowerLeftCorner(), false); * }*/ // draw dividers for call graph /*if (ViewLayout == LayoutType.CallGraph) * { * if (ShowRightOutsideArea) * buffer.DrawLine(CallDividerPen, RightDivider, 0, RightDivider, Height); * * if (ShowLeftOutsideArea) * buffer.DrawLine(CallDividerPen, LeftDivider, 0, LeftDivider, Height); * }*/ // draw dependency graph if (ViewLayout == LayoutType.CallGraph && (GraphMode == CallGraphMode.Dependencies || GraphMode == CallGraphMode.Init || GraphMode == CallGraphMode.Intermediates)) { foreach (var source in PositionMap.Values) { if (source.EdgesOut == null) { continue; } foreach (var to in source.EdgesOut) { if (!PositionMap.ContainsKey(to)) { continue; } var destination = PositionMap[to]; int penWidth = (source.Focused || destination.Focused) ? 2 : 1; if ((!DrawCallGraphVertically && source.AreaF.X < destination.AreaF.X) || (DrawCallGraphVertically && source.AreaF.Y < destination.AreaF.Y)) { DrawGraphEdge(penWidth, XColors.CallOutColor, source, destination); } else { DrawGraphEdge(penWidth, XColors.CallInColor, source, destination); } } } } // draw call graph if (XRay.FlowTracking && ViewLayout != LayoutType.Timeline) { foreach (var source in PositionMap.Values) { if (source.XNode.CallsOut == null) { continue; } if (ViewLayout == LayoutType.TreeMap && source.ObjType == XObjType.Class && ShowMethods) { continue; } if (ViewLayout == LayoutType.CallGraph && GraphMode != CallGraphMode.Init && source.ObjType == XObjType.Class && ShowMethods) { continue; } foreach (var call in source.XNode.CallsOut) { if (!PositionMap.ContainsKey(call.Destination)) { continue; } if (ShowThreads != null && !call.ThreadIDs.Any(id => ShowThreads.Contains(id))) { continue; } var destination = PositionMap[call.Destination]; // if there are items we're filtering on then only show calls to those nodes if (FilteredNodes.Count > 0 && !IsNodeFiltered(true, source) && !IsNodeFiltered(true, destination)) { continue; } // do after select filter so we can have ignored nodes inside selected, but not the otherway around if (IgnoredNodes.Count > 0 && IsNodeFiltered(false, source) || IsNodeFiltered(false, destination)) { continue; } int lineWidth = (source.Focused || destination.Focused) ? 2 : 1; if (call.StillInside > 0 && ShowCalls) { if (ViewLayout == LayoutType.TreeMap) { Renderer.DrawCallLine(XColors.HoldingCallColor, lineWidth, source.CenterF, destination.CenterF, false, source, destination); } else if (ViewLayout == LayoutType.CallGraph) { DrawGraphEdge(lineWidth, XColors.HoldingCallColor, source, destination); } } else if (ShowAllCalls && GraphMode != CallGraphMode.Intermediates && GraphMode != CallGraphMode.Init) { if (ViewLayout == LayoutType.TreeMap) { var callSource = PositionMap[call.Source]; var callDest = PositionMap[call.Destination]; PointF start = callSource.CenterF; PointF end = callDest.CenterF; PointF mid = new PointF(start.X + (end.X - start.X) / 2, start.Y + (end.Y - start.Y) / 2); Renderer.DrawCallLine(XColors.CallOutColor, lineWidth, start, mid, false, callSource, callDest); Renderer.DrawCallLine(XColors.CallInColor, lineWidth, mid, end, false, callSource, callDest); } else if (ViewLayout == LayoutType.CallGraph) { if ((!DrawCallGraphVertically && source.AreaF.X < destination.AreaF.X) || (DrawCallGraphVertically && source.AreaF.Y < destination.AreaF.Y)) { DrawGraphEdge(lineWidth, XColors.CallOutColor, source, destination); } else { DrawGraphEdge(lineWidth, XColors.CallInColor, source, destination); } } } if (call.Hit > 0 && ShowCalls) { var color = XColors.CallPenColors[call.Hit]; if (ViewLayout == LayoutType.TreeMap) { Renderer.DrawCallLine(color, lineWidth, source.CenterF, destination.CenterF, true, source, destination); } else if (ViewLayout == LayoutType.CallGraph) { DrawGraphEdge(lineWidth, color, source, destination, true); } } } } } XRay.DashOffset = (XRay.DashOffset == 0) ? 2 : XRay.DashOffset - 1; // draw mouse over label if (ViewLayout != LayoutType.Timeline) { DrawFooterLabel(); } DrawToolTip(); }