From f89aa056d5a782e4633e3f531bf44d25f8878cb5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 02:17:04 +0000 Subject: [PATCH 1/7] Initial plan From be6c5362f16d30b98ec8bae1be35c5add49b415a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 02:25:06 +0000 Subject: [PATCH 2/7] Add validation for FlameGraph export with invalid canvas size Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com> --- .../StackViewer/StackWindowTests.cs | 34 +++++++++++++++++++ src/PerfView/StackViewer/FlameGraph.cs | 13 ++++++- src/PerfView/StackViewer/StackWindow.xaml.cs | 14 ++++++-- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/PerfView.Tests/StackViewer/StackWindowTests.cs b/src/PerfView.Tests/StackViewer/StackWindowTests.cs index 4ba2ea817..c7b9edc0b 100644 --- a/src/PerfView.Tests/StackViewer/StackWindowTests.cs +++ b/src/PerfView.Tests/StackViewer/StackWindowTests.cs @@ -859,5 +859,39 @@ public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verbo return frameIndex.ToString(); } } + + [WpfFact] + [WorkItem(2308, "https://github.com/Microsoft/perfview/issues/2308")] + public void TestExportFlameGraphWithInvalidCanvasSize() + { + // Create a canvas with zero size (simulating an unrendered or collapsed canvas) + var canvas = new Canvas(); + canvas.Width = 0; + canvas.Height = 0; + canvas.Measure(new Size(0, 0)); + canvas.Arrange(new Rect(0, 0, 0, 0)); + + var tempFile = System.IO.Path.GetTempFileName(); + try + { + // Attempt to export should throw InvalidOperationException with a meaningful message + var exception = Assert.Throws(() => + { + FlameGraph.Export(canvas, tempFile); + }); + + // Verify the exception message is helpful + Assert.Contains("canvas has an invalid size", exception.Message); + Assert.Contains("width=0", exception.Message); + Assert.Contains("height=0", exception.Message); + } + finally + { + if (File.Exists(tempFile)) + { + File.Delete(tempFile); + } + } + } } } diff --git a/src/PerfView/StackViewer/FlameGraph.cs b/src/PerfView/StackViewer/FlameGraph.cs index d8588a169..76c1796a0 100644 --- a/src/PerfView/StackViewer/FlameGraph.cs +++ b/src/PerfView/StackViewer/FlameGraph.cs @@ -83,7 +83,18 @@ public static IEnumerable Calculate(CallTree callTree, double maxWidth 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); + int width = (int)rectangle.Right; + int height = (int)rectangle.Bottom; + + // Validate that the canvas has a valid size before attempting to export + if (width <= 0 || height <= 0) + { + throw new InvalidOperationException( + $"Cannot export flame graph: the canvas has an invalid size (width={width}, height={height}). " + + "Please ensure the flame graph is visible and has been rendered before attempting to export."); + } + + var renderTargetBitmap = new RenderTargetBitmap(width, height, 96d, 96d, PixelFormats.Default); renderTargetBitmap.Render(flameGraphCanvas); var pngEncoder = new PngBitmapEncoder(); diff --git a/src/PerfView/StackViewer/StackWindow.xaml.cs b/src/PerfView/StackViewer/StackWindow.xaml.cs index 3c907dd48..6ddccbcca 100644 --- a/src/PerfView/StackViewer/StackWindow.xaml.cs +++ b/src/PerfView/StackViewer/StackWindow.xaml.cs @@ -2764,10 +2764,18 @@ private void DoSaveFlameGraph(object sender, RoutedEventArgs e) var result = saveDialog.ShowDialog(); if (result == true) { - if (FlameGraphCanvas.IsEmpty || m_RedrawFlameGraphWhenItBecomesVisible) - RedrawFlameGraph(); + try + { + if (FlameGraphCanvas.IsEmpty || m_RedrawFlameGraphWhenItBecomesVisible) + RedrawFlameGraph(); - FlameGraph.Export(FlameGraphCanvas, saveDialog.FileName); + FlameGraph.Export(FlameGraphCanvas, saveDialog.FileName); + StatusBar.Log("Saved flame graph to " + saveDialog.FileName); + } + catch (InvalidOperationException ex) + { + StatusBar.LogError("Failed to save flame graph: " + ex.Message); + } } } From 1320239ee62d8702ebd8e62cc96dc98f24f73a57 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 02:27:34 +0000 Subject: [PATCH 3/7] Address code review feedback - use string interpolation and consistent namespace usage Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com> --- src/PerfView.Tests/StackViewer/StackWindowTests.cs | 2 +- src/PerfView/StackViewer/StackWindow.xaml.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PerfView.Tests/StackViewer/StackWindowTests.cs b/src/PerfView.Tests/StackViewer/StackWindowTests.cs index c7b9edc0b..71f02fff7 100644 --- a/src/PerfView.Tests/StackViewer/StackWindowTests.cs +++ b/src/PerfView.Tests/StackViewer/StackWindowTests.cs @@ -871,7 +871,7 @@ public void TestExportFlameGraphWithInvalidCanvasSize() canvas.Measure(new Size(0, 0)); canvas.Arrange(new Rect(0, 0, 0, 0)); - var tempFile = System.IO.Path.GetTempFileName(); + var tempFile = Path.GetTempFileName(); try { // Attempt to export should throw InvalidOperationException with a meaningful message diff --git a/src/PerfView/StackViewer/StackWindow.xaml.cs b/src/PerfView/StackViewer/StackWindow.xaml.cs index 6ddccbcca..b70ce415f 100644 --- a/src/PerfView/StackViewer/StackWindow.xaml.cs +++ b/src/PerfView/StackViewer/StackWindow.xaml.cs @@ -2770,11 +2770,11 @@ private void DoSaveFlameGraph(object sender, RoutedEventArgs e) RedrawFlameGraph(); FlameGraph.Export(FlameGraphCanvas, saveDialog.FileName); - StatusBar.Log("Saved flame graph to " + saveDialog.FileName); + StatusBar.Log($"Saved flame graph to {saveDialog.FileName}"); } catch (InvalidOperationException ex) { - StatusBar.LogError("Failed to save flame graph: " + ex.Message); + StatusBar.LogError($"Failed to save flame graph: {ex.Message}"); } } } From 0699b4a254aeee986725bce96aa0181fbba59c73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 04:57:23 +0000 Subject: [PATCH 4/7] Change exception type to ArgumentOutOfRangeException and shorten error message Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com> --- src/PerfView.Tests/StackViewer/StackWindowTests.cs | 4 ++-- src/PerfView/StackViewer/FlameGraph.cs | 6 +++--- src/PerfView/StackViewer/StackWindow.xaml.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PerfView.Tests/StackViewer/StackWindowTests.cs b/src/PerfView.Tests/StackViewer/StackWindowTests.cs index 71f02fff7..1eb3d44df 100644 --- a/src/PerfView.Tests/StackViewer/StackWindowTests.cs +++ b/src/PerfView.Tests/StackViewer/StackWindowTests.cs @@ -874,8 +874,8 @@ public void TestExportFlameGraphWithInvalidCanvasSize() var tempFile = Path.GetTempFileName(); try { - // Attempt to export should throw InvalidOperationException with a meaningful message - var exception = Assert.Throws(() => + // Attempt to export should throw ArgumentOutOfRangeException with a meaningful message + var exception = Assert.Throws(() => { FlameGraph.Export(canvas, tempFile); }); diff --git a/src/PerfView/StackViewer/FlameGraph.cs b/src/PerfView/StackViewer/FlameGraph.cs index 76c1796a0..5633617e7 100644 --- a/src/PerfView/StackViewer/FlameGraph.cs +++ b/src/PerfView/StackViewer/FlameGraph.cs @@ -89,9 +89,9 @@ public static void Export(Canvas flameGraphCanvas, string filePath) // Validate that the canvas has a valid size before attempting to export if (width <= 0 || height <= 0) { - throw new InvalidOperationException( - $"Cannot export flame graph: the canvas has an invalid size (width={width}, height={height}). " + - "Please ensure the flame graph is visible and has been rendered before attempting to export."); + throw new ArgumentOutOfRangeException( + "flameGraphCanvas", + $"Canvas has an invalid size (width={width}, height={height}). Please ensure the flame graph is visible and has been rendered before attempting to export."); } var renderTargetBitmap = new RenderTargetBitmap(width, height, 96d, 96d, PixelFormats.Default); diff --git a/src/PerfView/StackViewer/StackWindow.xaml.cs b/src/PerfView/StackViewer/StackWindow.xaml.cs index b70ce415f..13257f58b 100644 --- a/src/PerfView/StackViewer/StackWindow.xaml.cs +++ b/src/PerfView/StackViewer/StackWindow.xaml.cs @@ -2772,7 +2772,7 @@ private void DoSaveFlameGraph(object sender, RoutedEventArgs e) FlameGraph.Export(FlameGraphCanvas, saveDialog.FileName); StatusBar.Log($"Saved flame graph to {saveDialog.FileName}"); } - catch (InvalidOperationException ex) + catch (ArgumentOutOfRangeException ex) { StatusBar.LogError($"Failed to save flame graph: {ex.Message}"); } From f684ba5264e91d8f0d8aec38cb45c9fca9af148b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 04:58:45 +0000 Subject: [PATCH 5/7] Fix capitalization in error message for consistency Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com> --- src/PerfView/StackViewer/FlameGraph.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PerfView/StackViewer/FlameGraph.cs b/src/PerfView/StackViewer/FlameGraph.cs index 5633617e7..e6effc899 100644 --- a/src/PerfView/StackViewer/FlameGraph.cs +++ b/src/PerfView/StackViewer/FlameGraph.cs @@ -91,7 +91,7 @@ public static void Export(Canvas flameGraphCanvas, string filePath) { throw new ArgumentOutOfRangeException( "flameGraphCanvas", - $"Canvas has an invalid size (width={width}, height={height}). Please ensure the flame graph is visible and has been rendered before attempting to export."); + $"canvas has an invalid size (width={width}, height={height}). Please ensure the flame graph is visible and has been rendered before attempting to export."); } var renderTargetBitmap = new RenderTargetBitmap(width, height, 96d, 96d, PixelFormats.Default); From 9f9e8720eb9d857d160aa905461f6640ea945e45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 05:17:22 +0000 Subject: [PATCH 6/7] Capitalize 'Canvas' in error message per review feedback Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com> --- src/PerfView.Tests/StackViewer/StackWindowTests.cs | 2 +- src/PerfView/StackViewer/FlameGraph.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PerfView.Tests/StackViewer/StackWindowTests.cs b/src/PerfView.Tests/StackViewer/StackWindowTests.cs index 1eb3d44df..4cfd0821e 100644 --- a/src/PerfView.Tests/StackViewer/StackWindowTests.cs +++ b/src/PerfView.Tests/StackViewer/StackWindowTests.cs @@ -881,7 +881,7 @@ public void TestExportFlameGraphWithInvalidCanvasSize() }); // Verify the exception message is helpful - Assert.Contains("canvas has an invalid size", exception.Message); + Assert.Contains("Canvas has an invalid size", exception.Message); Assert.Contains("width=0", exception.Message); Assert.Contains("height=0", exception.Message); } diff --git a/src/PerfView/StackViewer/FlameGraph.cs b/src/PerfView/StackViewer/FlameGraph.cs index e6effc899..5633617e7 100644 --- a/src/PerfView/StackViewer/FlameGraph.cs +++ b/src/PerfView/StackViewer/FlameGraph.cs @@ -91,7 +91,7 @@ public static void Export(Canvas flameGraphCanvas, string filePath) { throw new ArgumentOutOfRangeException( "flameGraphCanvas", - $"canvas has an invalid size (width={width}, height={height}). Please ensure the flame graph is visible and has been rendered before attempting to export."); + $"Canvas has an invalid size (width={width}, height={height}). Please ensure the flame graph is visible and has been rendered before attempting to export."); } var renderTargetBitmap = new RenderTargetBitmap(width, height, 96d, 96d, PixelFormats.Default); From 7af7d62fcc004b3bcc98d07bc76a82a1b205d1ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 13:43:06 +0000 Subject: [PATCH 7/7] Use nameof() for parameter name in ArgumentOutOfRangeException Co-authored-by: leculver <8496639+leculver@users.noreply.github.com> --- src/PerfView/StackViewer/FlameGraph.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PerfView/StackViewer/FlameGraph.cs b/src/PerfView/StackViewer/FlameGraph.cs index 5633617e7..e64ecb6a6 100644 --- a/src/PerfView/StackViewer/FlameGraph.cs +++ b/src/PerfView/StackViewer/FlameGraph.cs @@ -90,7 +90,7 @@ public static void Export(Canvas flameGraphCanvas, string filePath) if (width <= 0 || height <= 0) { throw new ArgumentOutOfRangeException( - "flameGraphCanvas", + nameof(flameGraphCanvas), $"Canvas has an invalid size (width={width}, height={height}). Please ensure the flame graph is visible and has been rendered before attempting to export."); }