Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 0 additions & 48 deletions .github/workflows/check-version-bump.yml

This file was deleted.

20 changes: 0 additions & 20 deletions Dashboard/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,6 @@ private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)

private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
/* Silently swallow Hardcodet TrayToolTip race condition (issue #422).
The crash occurs in Popup.CreateWindow when showing the custom visual tooltip
and is harmless — the tooltip simply doesn't show that one time. */
if (IsTrayToolTipCrash(e.Exception))
{
Logger.Warning("Suppressed Hardcodet TrayToolTip crash (issue #422)");
e.Handled = true;
return;
}

Logger.Error("Unhandled Dispatcher Exception", e.Exception);

MessageBox.Show(
Expand All @@ -124,16 +114,6 @@ private void OnUnobservedTaskException(object? sender, UnobservedTaskExceptionEv
e.SetObserved(); // Prevent process termination
}

/// <summary>
/// Detects the Hardcodet TrayToolTip race condition crash (issue #422).
/// </summary>
private static bool IsTrayToolTipCrash(Exception ex)
{
return ex is ArgumentException
&& ex.Message.Contains("VisualTarget")
&& ex.StackTrace?.Contains("TaskbarIcon") == true;
}

private void CreateCrashDump(Exception? exception)
{
try
Expand Down
6 changes: 2 additions & 4 deletions Dashboard/Controls/PlanViewerControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,7 @@ private void ShowPropertiesPanel(PlanNode node)

// Header
var headerText = node.PhysicalOp;
if (node.LogicalOp != node.PhysicalOp && !string.IsNullOrEmpty(node.LogicalOp)
&& !node.PhysicalOp.Contains(node.LogicalOp, StringComparison.OrdinalIgnoreCase))
if (node.LogicalOp != node.PhysicalOp && !string.IsNullOrEmpty(node.LogicalOp))
headerText += $" ({node.LogicalOp})";
PropertiesHeader.Text = headerText;
PropertiesSubHeader.Text = $"Node ID: {node.NodeId}";
Expand Down Expand Up @@ -1482,8 +1481,7 @@ private ToolTip BuildNodeTooltip(PlanNode node)

// Header
var headerText = node.PhysicalOp;
if (node.LogicalOp != node.PhysicalOp && !string.IsNullOrEmpty(node.LogicalOp)
&& !node.PhysicalOp.Contains(node.LogicalOp, StringComparison.OrdinalIgnoreCase))
if (node.LogicalOp != node.PhysicalOp && !string.IsNullOrEmpty(node.LogicalOp))
headerText += $" ({node.LogicalOp})";
stack.Children.Add(new TextBlock
{
Expand Down
45 changes: 1 addition & 44 deletions Dashboard/Controls/ResourceMetricsContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,7 @@ private void SetupChartContextMenus()
TabHelpers.SetupChartContextMenu(PerfmonCountersChart, "Perfmon_Counters", "collect.perfmon_stats");

// Wait Stats Detail chart
var waitStatsMenu = TabHelpers.SetupChartContextMenu(WaitStatsDetailChart, "Wait_Stats_Detail", "collect.wait_stats");
AddWaitDrillDownMenuItem(WaitStatsDetailChart, waitStatsMenu);
TabHelpers.SetupChartContextMenu(WaitStatsDetailChart, "Wait_Stats_Detail", "collect.wait_stats");
}

/// <summary>
Expand Down Expand Up @@ -1814,48 +1813,6 @@ private async void WaitType_CheckChanged(object sender, RoutedEventArgs e)
await UpdateWaitStatsDetailChartAsync();
}

private void AddWaitDrillDownMenuItem(ScottPlot.WPF.WpfPlot chart, ContextMenu contextMenu)
{
contextMenu.Items.Insert(0, new Separator());
var drillDownItem = new MenuItem { Header = "Show Queries With This Wait" };
drillDownItem.Click += ShowQueriesForWaitType_Click;
contextMenu.Items.Insert(0, drillDownItem);

contextMenu.Opened += (s, _) =>
{
var pos = System.Windows.Input.Mouse.GetPosition(chart);
var nearest = _waitStatsHover?.GetNearestSeries(pos);
if (nearest.HasValue)
{
drillDownItem.Tag = (nearest.Value.Label, nearest.Value.Time);
drillDownItem.Header = $"Show Queries With {nearest.Value.Label.Replace("_", "__")}";
drillDownItem.IsEnabled = true;
}
else
{
drillDownItem.Tag = null;
drillDownItem.Header = "Show Queries With This Wait";
drillDownItem.IsEnabled = false;
}
};
}

private void ShowQueriesForWaitType_Click(object sender, RoutedEventArgs e)
{
if (sender is not MenuItem menuItem) return;
if (menuItem.Tag is not ValueTuple<string, DateTime> tag) return;
if (_databaseService == null) return;

// ±15 minute window around the clicked point
var fromDate = tag.Item2.AddMinutes(-15);
var toDate = tag.Item2.AddMinutes(15);

var window = new WaitDrillDownWindow(
_databaseService, tag.Item1, 1, fromDate, toDate);
window.Owner = Window.GetWindow(this);
window.ShowDialog();
}

private void WaitStatsMetric_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_allWaitStatsDetailData != null)
Expand Down
7 changes: 2 additions & 5 deletions Dashboard/Converters/QueryTextCleanupConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace PerformanceMonitorDashboard.Converters
{
public partial class QueryTextCleanupConverter : IValueConverter
public class QueryTextCleanupConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Expand All @@ -28,7 +28,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn
text = text.Replace("\t", " ", StringComparison.Ordinal);

// Replace multiple spaces with single space
text = MultipleSpacesRegExp().Replace(text, " ");
text = Regex.Replace(text, @"\s+", " ");

// Trim leading/trailing whitespace
text = text.Trim();
Expand All @@ -40,8 +40,5 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu
{
throw new NotImplementedException();
}

[GeneratedRegex(@"\s+")]
private static partial Regex MultipleSpacesRegExp();
}
}
8 changes: 4 additions & 4 deletions Dashboard/Dashboard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
<UseWPF>true</UseWPF>
<AssemblyName>PerformanceMonitorDashboard</AssemblyName>
<Product>SQL Server Performance Monitor Dashboard</Product>
<Version>2.2.0</Version>
<AssemblyVersion>2.2.0.0</AssemblyVersion>
<FileVersion>2.2.0.0</FileVersion>
<InformationalVersion>2.2.0</InformationalVersion>
<Version>2.1.0</Version>
<AssemblyVersion>2.1.0.0</AssemblyVersion>
<FileVersion>2.1.0.0</FileVersion>
<InformationalVersion>2.1.0</InformationalVersion>
<Company>Darling Data, LLC</Company>
<Copyright>Copyright © 2026 Darling Data, LLC</Copyright>
<ApplicationIcon>EDD.ico</ApplicationIcon>
Expand Down
49 changes: 2 additions & 47 deletions Dashboard/Helpers/ChartHoverHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,50 +61,6 @@ public ChartHoverHelper(ScottPlot.WPF.WpfPlot chart, string unit)
public void Add(ScottPlot.Plottables.Scatter scatter, string label) =>
_scatters.Add((scatter, label));

/// <summary>
/// Returns the nearest series label and data-point time for the given mouse position,
/// or null if no series is close enough.
/// </summary>
public (string Label, DateTime Time)? GetNearestSeries(Point mousePos)
{
if (_scatters.Count == 0) return null;
try
{
var dpi = VisualTreeHelper.GetDpi(_chart);
var pixel = new ScottPlot.Pixel(
(float)(mousePos.X * dpi.DpiScaleX),
(float)(mousePos.Y * dpi.DpiScaleY));
var mouseCoords = _chart.Plot.GetCoordinates(pixel);

double bestYDistance = double.MaxValue;
ScottPlot.DataPoint bestPoint = default;
string bestLabel = "";
bool found = false;

foreach (var (scatter, label) in _scatters)
{
var nearest = scatter.Data.GetNearest(mouseCoords, _chart.Plot.LastRender);
if (!nearest.IsReal) continue;
var nearestPixel = _chart.Plot.GetPixel(
new ScottPlot.Coordinates(nearest.X, nearest.Y));
double dx = Math.Abs(nearestPixel.X - pixel.X);
double dy = Math.Abs(nearestPixel.Y - pixel.Y);
if (dx < 80 && dy < bestYDistance)
{
bestYDistance = dy;
bestPoint = nearest;
bestLabel = label;
found = true;
}
}

if (found)
return (bestLabel, DateTime.FromOADate(bestPoint.X));
}
catch { }
return null;
}

private void OnMouseMove(object sender, MouseEventArgs e)
{
if (_scatters.Count == 0) return;
Expand All @@ -115,10 +71,9 @@ private void OnMouseMove(object sender, MouseEventArgs e)
try
{
var pos = e.GetPosition(_chart);
var dpi = VisualTreeHelper.GetDpi(_chart);
var pixel = new ScottPlot.Pixel(
(float)(pos.X * dpi.DpiScaleX),
(float)(pos.Y * dpi.DpiScaleY));
(float)(pos.X * _chart.DisplayScale),
(float)(pos.Y * _chart.DisplayScale));
var mouseCoords = _chart.Plot.GetCoordinates(pixel);

/* Use X-axis (time) proximity as the primary filter, Y-axis distance
Expand Down
7 changes: 2 additions & 5 deletions Dashboard/Helpers/DateFilterHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace PerformanceMonitorDashboard.Helpers
{
public static partial class DateFilterHelper
public static class DateFilterHelper
{
public static bool MatchesFilter(object? value, string? filterText)
{
Expand Down Expand Up @@ -148,7 +148,7 @@ private static bool TryConvertToDateTime(object value, out DateTime result)
}

// "last N hours/days/weeks" expressions
var lastMatch = LastNHoursDaysWeeksMonthsRegExp().Match(expressionLower);
var lastMatch = Regex.Match(expressionLower, @"last\s+(\d+)\s+(hour|hours|day|days|week|weeks|month|months)");
if (lastMatch.Success)
{
int count = int.Parse(lastMatch.Groups[1].Value, CultureInfo.InvariantCulture);
Expand Down Expand Up @@ -231,8 +231,5 @@ private static bool IsRelativeExpression(string expression)
expression == "tomorrow" ||
Regex.IsMatch(expression, @"last\s+\d+\s+(hour|hours|day|days|week|weeks|month|months)");
}

[GeneratedRegex(@"last\s+(\d+)\s+(hour|hours|day|days|week|weeks|month|months)")]
private static partial Regex LastNHoursDaysWeeksMonthsRegExp();
}
}
4 changes: 1 addition & 3 deletions Dashboard/Helpers/TabHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ public static string FormatForExport(object? value)
/// <param name="chart">The WpfPlot chart control</param>
/// <param name="chartName">A descriptive name for the chart (used in filenames)</param>
/// <param name="dataSource">Optional SQL view/table name that populates this chart</param>
public static ContextMenu SetupChartContextMenu(WpfPlot chart, string chartName, string? dataSource = null)
public static void SetupChartContextMenu(WpfPlot chart, string chartName, string? dataSource = null)
{
var contextMenu = new ContextMenu();

Expand Down Expand Up @@ -786,8 +786,6 @@ public static ContextMenu SetupChartContextMenu(WpfPlot chart, string chartName,
chart.Plot.Axes.AutoScale();
chart.Refresh();
};

return contextMenu;
}

/// <summary>
Expand Down
Loading
Loading