From ac31c4a0cddfb95cc38df91dbf64aa7b38e6cd7f Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 5 Jan 2018 21:12:19 +0100 Subject: [PATCH 1/2] Flame Graph --- src/PerfView/FlameGraph.cs | 160 ++++++++++++++++++ src/PerfView/StackViewer/StackWindow.xaml | 16 ++ src/PerfView/StackViewer/StackWindow.xaml.cs | 71 +++++++- src/PerfView/SupportFiles/UsersGuide.htm | 16 ++ .../SupportFiles/images/FlameGraphView.png | Bin 0 -> 55225 bytes 5 files changed, 257 insertions(+), 6 deletions(-) create mode 100644 src/PerfView/FlameGraph.cs create mode 100644 src/PerfView/SupportFiles/images/FlameGraphView.png diff --git a/src/PerfView/FlameGraph.cs b/src/PerfView/FlameGraph.cs new file mode 100644 index 000000000..7708fe418 --- /dev/null +++ b/src/PerfView/FlameGraph.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Microsoft.Diagnostics.Tracing.Stacks; + +namespace PerfView +{ + public static class FlameGraph + { + private static readonly FontFamily FontFamily = new FontFamily("Consolas"); + private static readonly Brush[] Brushes = GenerateBrushes(new Random(12345)); + + /// + /// (X=0, Y=0) is the left bottom corner of the canvas + /// + public struct FlameBox + { + public readonly double Width, Height, X, Y; + public readonly CallTreeNode Node; + + public FlameBox(CallTreeNode node, double width, double height, double x, double y) + { + Node = node; + Width = width; + Height = height; + X = x; + Y = y; + } + } + + private struct FlamePair + { + public readonly FlameBox ParentBox; + public readonly CallTreeNode Node; + + public FlamePair(FlameBox parentBox, CallTreeNode node) + { + ParentBox = parentBox; + Node = node; + } + } + + public static IEnumerable Calculate(CallTree callTree, double maxWidth, double maxHeight) + { + double maxDepth = GetMaxDepth(callTree.Root); + double boxHeight = maxHeight / maxDepth; + double pixelsPerIncusiveSample = maxWidth / callTree.Root.InclusiveMetric; + + var rootBox = new FlameBox(callTree.Root, maxWidth, boxHeight, 0, 0); + yield return rootBox; + + var nodesToVisit = new Queue(); + nodesToVisit.Enqueue(new FlamePair(rootBox, callTree.Root)); + + while (nodesToVisit.Count > 0) + { + var current = nodesToVisit.Dequeue(); + var parentBox = current.ParentBox; + var currentNode = current.Node; + + double nextBoxX = (parentBox.Width - (currentNode.Callees.Sum(child => child.InclusiveMetric) * pixelsPerIncusiveSample)) / 2.0; // centering the starting point + + foreach (var child in currentNode.Callees) + { + double childBoxWidth = child.InclusiveMetric * pixelsPerIncusiveSample; + var childBox = new FlameBox(child, childBoxWidth, boxHeight, parentBox.X + nextBoxX, parentBox.Y + boxHeight); + nextBoxX += childBoxWidth; + + if (child.Callees != null) + nodesToVisit.Enqueue(new FlamePair(childBox, child)); + + yield return childBox; + } + } + } + + public static void Draw(IEnumerable boxes, Canvas canvas) + { + canvas.Children.Clear(); + + int index = 0; + foreach (var box in boxes) + { + FrameworkElement rectangle = CreateRectangle(box, ++index); + + Canvas.SetLeft(rectangle, box.X); + Canvas.SetBottom(rectangle, box.Y); + canvas.Children.Add(rectangle); + } + } + + public static void Export(Canvas flameGraphCanvas, string filePath) + { + var rectangle = new Rect(flameGraphCanvas.RenderSize); + var renderTargetBitmap = new RenderTargetBitmap((int)rectangle.Right, (int)rectangle.Bottom, 96d, 96d, PixelFormats.Default); + renderTargetBitmap.Render(flameGraphCanvas); + + var pngEncoder = new PngBitmapEncoder(); + pngEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap)); + + using (var file = System.IO.File.Create(filePath)) + pngEncoder.Save(file); + } + + private static Brush[] GenerateBrushes(Random random) + => Enumerable.Range(0, 100) + .Select(_ => (Brush)new SolidColorBrush( + Color.FromRgb( + (byte)(205.0 + 50.0 * random.NextDouble()), + (byte)(230.0 * random.NextDouble()), + (byte)(55.0 * random.NextDouble())))) + .ToArray(); + + private static double GetMaxDepth(CallTreeNode callTree) + { + double deepest = 0; + + if (callTree.Callees != null) + foreach (var callee in callTree.Callees) + deepest = Math.Max(deepest, GetMaxDepth(callee)); + + return deepest + 1; + } + + private static FrameworkElement CreateRectangle(FlameBox box, int index) + { + var tooltip = $"Method: {box.Node.DisplayName} ({box.Node.InclusiveCount} inclusive samples, {box.Node.InclusiveMetricPercent:F}%)"; + var background = Brushes[++index % Brushes.Length]; // in the future, the color could be chosen according to the belonging of the method (JIT, GC, user code, OS etc) + + // for small boxes we create Rectangles, because they are much faster (too many TextBlocks === bad perf) + // also for small rectangles it's impossible to read the name of the method anyway (only few characters are printed) + if (box.Width < 50) + return new Rectangle + { + Height = box.Height, + Width = box.Width, + Fill = background, + ToolTip = new ToolTip { Content = tooltip }, + DataContext = box.Node + }; + + return new TextBlock + { + Height = box.Height, + Width = box.Width, + Background = background, + ToolTip = new ToolTip { Content = tooltip }, + Text = box.Node.DisplayName, + DataContext = box.Node, + FontFamily = FontFamily, + FontSize = Math.Min(20.0, box.Height) + }; + } + } +} \ No newline at end of file diff --git a/src/PerfView/StackViewer/StackWindow.xaml b/src/PerfView/StackViewer/StackWindow.xaml index 424b4a17d..6c90763bb 100644 --- a/src/PerfView/StackViewer/StackWindow.xaml +++ b/src/PerfView/StackViewer/StackWindow.xaml @@ -95,6 +95,7 @@ + @@ -465,6 +466,21 @@ + + + + + Flame Graph ? + + + + + + + + + + diff --git a/src/PerfView/StackViewer/StackWindow.xaml.cs b/src/PerfView/StackViewer/StackWindow.xaml.cs index 2f0037ed9..7fc1b8a7c 100644 --- a/src/PerfView/StackViewer/StackWindow.xaml.cs +++ b/src/PerfView/StackViewer/StackWindow.xaml.cs @@ -28,6 +28,7 @@ using System.Threading; using PerfView.GuiUtilities; using Utilities; +using Path = System.IO.Path; namespace PerfView { @@ -411,6 +412,8 @@ public void SetStackSource(StackSource newSource, Action onComplete = null) cumMax / 1000000, controller.GetStartTimeForBucket((HistogramCharacterIndex)cumMaxIdx)); } + RedrawFlameGraph(); + TopStats.Text = stats; // TODO this is a bit of a hack, as it might replace other instances of the string. @@ -2529,6 +2532,55 @@ private void NotesTab_LostFocus(object sender, RoutedEventArgs e) m_NotesTabActive = false; } + private void FlameGraphTab_GotFocus(object sender, RoutedEventArgs e) + { + if (FlameGraphCanvas.Children.Count == 0) + RedrawFlameGraph(); + } + + private void FlameGraphCanvas_SizeChanged(object sender, SizeChangedEventArgs e) => RedrawFlameGraph(); + + private void RedrawFlameGraph() + => FlameGraph.Draw( + CallTree.Root.HasChildren + ? FlameGraph.Calculate(CallTree, FlameGraphCanvas.ActualWidth, FlameGraphCanvas.ActualHeight) + : Enumerable.Empty(), + FlameGraphCanvas); + + private void FlameGraphCanvas_MouseMove(object sender, MouseEventArgs e) + { + if (StatusBar.LoggedError || FlameGraphCanvas.Children.Count == 0) + return; + + var pointed = FlameGraphCanvas.Children.OfType().FirstOrDefault(box => box.IsMouseOver); + var toolTip = pointed?.ToolTip as ToolTip; + if (toolTip != null) + StatusBar.Status = toolTip.Content as string; + } + + private void DoSaveFlameGraph(object sender, RoutedEventArgs e) + { + var saveDialog = new Microsoft.Win32.SaveFileDialog(); + var baseName = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(DataSource.FilePath)); + + for (int i = 1; ; i++) + { + saveDialog.FileName = baseName + ".flameGraph" + i.ToString() + ".png"; + if (!File.Exists(saveDialog.FileName)) + break; + } + saveDialog.InitialDirectory = Path.GetDirectoryName(DataSource.FilePath); + saveDialog.Title = "File to save flame graph"; + saveDialog.DefaultExt = ".png"; + saveDialog.Filter = "Image files (*.png)|*.png|All files (*.*)|*.*"; + saveDialog.AddExtension = true; + saveDialog.OverwritePrompt = true; + + var result = saveDialog.ShowDialog(); + if (result == true) + FlameGraph.Export(FlameGraphCanvas, saveDialog.FileName); + } + private TabItem SelectedTab { get @@ -2543,8 +2595,11 @@ private TabItem SelectedTab return CallersTab; else if (CalleesTab.IsSelected) return CalleesTab; + else if (FlameGraphTab.IsSelected) + return FlameGraphTab; else if (NotesTab.IsSelected) return NotesTab; + Debug.Assert(false, "No tab selected!"); return null; } @@ -2601,22 +2656,25 @@ public StackWindowGuiState GuiState { switch (value.TabSelected) { - case "ByNameTab": + case nameof(ByNameTab): ByNameTab.IsSelected = true; break; - case "CallerCalleeTab": + case nameof(CallerCalleeTab): CallerCalleeTab.IsSelected = true; break; - case "CallTreeTab": + case nameof(CallTreeTab): CallTreeTab.IsSelected = true; break; - case "CalleesTab": + case nameof(CalleesTab): CalleesTab.IsSelected = true; break; - case "CallersTab": + case nameof(CallersTab): CallersTab.IsSelected = true; break; - case "NotesTab": + case nameof(FlameGraphTab): + FlameGraphTab.IsSelected = true; + break; + case nameof(NotesTab): NotesTab.IsSelected = true; break; } @@ -2656,6 +2714,7 @@ public StackWindowGuiState GuiState public static RoutedUICommand SaveCommand = new RoutedUICommand("Save", "Save", typeof(StackWindow), new InputGestureCollection() { new KeyGesture(Key.S, ModifierKeys.Control) }); public static RoutedUICommand SaveAsCommand = new RoutedUICommand("SaveAs", "SaveAs", typeof(StackWindow)); + public static RoutedUICommand SaveFlameGraphCommand = new RoutedUICommand("SaveFlameGraph", "SaveFlameGraph", typeof(StackWindow)); public static RoutedUICommand CancelCommand = new RoutedUICommand("Cancel", "Cancel", typeof(StackWindow), new InputGestureCollection() { new KeyGesture(Key.Escape) }); public static RoutedUICommand UpdateCommand = new RoutedUICommand("Update", "Update", typeof(StackWindow), diff --git a/src/PerfView/SupportFiles/UsersGuide.htm b/src/PerfView/SupportFiles/UsersGuide.htm index 93aef6929..2c9cebc06 100644 --- a/src/PerfView/SupportFiles/UsersGuide.htm +++ b/src/PerfView/SupportFiles/UsersGuide.htm @@ -3088,6 +3088,7 @@

See the tutorial for an example of using this view.   See stack viewer for more.  + See flame graph for different visual representation.

@@ -3246,6 +3247,21 @@

for more.

+

+ Flame Graph View +

+

+ The flame graph view shows the same data as call tree view, but using different visualization.    + It gives you very intelligible overview.   + The graph starts at the bottom. Each box represents a method in the stack. Every parent is the caller, children are the callees. + The wider the box, the more time it was on-CPU. The samples count is shown in the tooltip and in the bottom panel. + To change the content of the flame graph you need to apply the filters for call tree view.   +

+
+ FlameGraphView +
+
+

Notes View

diff --git a/src/PerfView/SupportFiles/images/FlameGraphView.png b/src/PerfView/SupportFiles/images/FlameGraphView.png new file mode 100644 index 0000000000000000000000000000000000000000..335452bd0f84dfe6accf2edff85e837077a35bf9 GIT binary patch literal 55225 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

(at{ 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 Y17ATKNbRN2!2kdN07*qoM6N<$g663}pa1{> literal 0 HcmV?d00001 From a4fdefcd14aab1bc7511fe7ac082ce7783c0a3dd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 8 Jan 2018 21:36:47 +0100 Subject: [PATCH 2/2] post code review improvements --- src/Directory.Build.props | 2 +- .../StackViewer/StackWindowTests.cs | 2 +- src/PerfView/PerfView.csproj | 5 ++++ src/PerfView/{ => StackViewer}/FlameGraph.cs | 0 src/PerfView/StackViewer/StackWindow.xaml.cs | 30 ++++++++++++++----- src/PerfView/SupportFiles/UsersGuide.htm | 10 +++++++ 6 files changed, 39 insertions(+), 10 deletions(-) rename src/PerfView/{ => StackViewer}/FlameGraph.cs (100%) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 152e100d0..737083a61 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -21,7 +21,7 @@ - 2.0.0.0 + 2.0.1.0 diff --git a/src/PerfView.Tests/StackViewer/StackWindowTests.cs b/src/PerfView.Tests/StackViewer/StackWindowTests.cs index d6c2e96f2..a9ae27b31 100644 --- a/src/PerfView.Tests/StackViewer/StackWindowTests.cs +++ b/src/PerfView.Tests/StackViewer/StackWindowTests.cs @@ -37,7 +37,7 @@ public Task TestIncludeItemOnByNameTabAsync() return TestIncludeItemAsync(KnownDataGrid.ByName); } - [WpfFact] + [WpfFact(Skip = "Failing with indexOutOfRange and Debug testing. See issue https://github.com/Microsoft/perfview/issues/354")] [WorkItem(316, "https://github.com/Microsoft/perfview/issues/316")] public Task TestIncludeItemOnCallerCalleeTabCallerAsync() { diff --git a/src/PerfView/PerfView.csproj b/src/PerfView/PerfView.csproj index 225bb88a1..b918e0687 100644 --- a/src/PerfView/PerfView.csproj +++ b/src/PerfView/PerfView.csproj @@ -415,6 +415,11 @@ false .\Images\ThreadTimeWithStartStop.png + + Non-Resx + false + .\Images\FlameGraphView.png + diff --git a/src/PerfView/FlameGraph.cs b/src/PerfView/StackViewer/FlameGraph.cs similarity index 100% rename from src/PerfView/FlameGraph.cs rename to src/PerfView/StackViewer/FlameGraph.cs diff --git a/src/PerfView/StackViewer/StackWindow.xaml.cs b/src/PerfView/StackViewer/StackWindow.xaml.cs index 7fc1b8a7c..2fc8d3a8a 100644 --- a/src/PerfView/StackViewer/StackWindow.xaml.cs +++ b/src/PerfView/StackViewer/StackWindow.xaml.cs @@ -412,7 +412,7 @@ public void SetStackSource(StackSource newSource, Action onComplete = null) cumMax / 1000000, controller.GetStartTimeForBucket((HistogramCharacterIndex)cumMaxIdx)); } - RedrawFlameGraph(); + RedrawFlameGraphIfVisible(); TopStats.Text = stats; @@ -2532,20 +2532,34 @@ private void NotesTab_LostFocus(object sender, RoutedEventArgs e) m_NotesTabActive = false; } + private bool m_RedrawFlameGraphWhenItBecomesVisible = false; + private void FlameGraphTab_GotFocus(object sender, RoutedEventArgs e) { - if (FlameGraphCanvas.Children.Count == 0) + if (FlameGraphCanvas.Children.Count == 0 || m_RedrawFlameGraphWhenItBecomesVisible) RedrawFlameGraph(); } - private void FlameGraphCanvas_SizeChanged(object sender, SizeChangedEventArgs e) => RedrawFlameGraph(); + private void FlameGraphCanvas_SizeChanged(object sender, SizeChangedEventArgs e) => RedrawFlameGraphIfVisible(); + + private void RedrawFlameGraphIfVisible() + { + if (FlameGraphTab.IsSelected) + RedrawFlameGraph(); + else + m_RedrawFlameGraphWhenItBecomesVisible = true; + } private void RedrawFlameGraph() - => FlameGraph.Draw( - CallTree.Root.HasChildren - ? FlameGraph.Calculate(CallTree, FlameGraphCanvas.ActualWidth, FlameGraphCanvas.ActualHeight) - : Enumerable.Empty(), - FlameGraphCanvas); + { + FlameGraph.Draw( + CallTree.Root.HasChildren + ? FlameGraph.Calculate(CallTree, FlameGraphCanvas.ActualWidth, FlameGraphCanvas.ActualHeight) + : Enumerable.Empty(), + FlameGraphCanvas); + + m_RedrawFlameGraphWhenItBecomesVisible = false; + } private void FlameGraphCanvas_MouseMove(object sender, MouseEventArgs e) { diff --git a/src/PerfView/SupportFiles/UsersGuide.htm b/src/PerfView/SupportFiles/UsersGuide.htm index 6143d3a09..d79394342 100644 --- a/src/PerfView/SupportFiles/UsersGuide.htm +++ b/src/PerfView/SupportFiles/UsersGuide.htm @@ -3256,6 +3256,7 @@

The graph starts at the bottom. Each box represents a method in the stack. Every parent is the caller, children are the callees. The wider the box, the more time it was on-CPU. The samples count is shown in the tooltip and in the bottom panel. To change the content of the flame graph you need to apply the filters for call tree view.   + To learn more about Flame Graphs please visit http://www.brendangregg.com/flamegraphs.html

FlameGraphView @@ -8093,6 +8094,15 @@

--> +
  • + Version 2.0.1 1/8/18 + +
  • Version 2.0.0 1/5/18