Repository navigation
Port PerformanceStudio's wait-row layout: an Auto benefit column that never clips - #4595
Merged
Merged
Conversation
… never clips Fixes #4576. Part of #4511. PerformanceStudio (erikdarlingdata/PerformanceStudio@78a3370, refined @80de6fc) fixed the Wait Stats card's rows so the flexible column is the duration text, not the bar; the wait-type name is capped at 150px with an ellipsis and a tooltip; and the trailing "up to N%" benefit sits in an Auto column so it is never the thing that gets clipped. Every row also carries a tooltip now. PerformanceMonitor's wait grid (PlanViewerControl.Properties.cs's ShowWaitStats, shared by the Darling Viewer and Lite) had none of this: a fixed name column sized off the longest wait-type string, a fixed 300px bar column, and no benefit column at all — the benefit percentage the analyzer already scores per wait type (BenefitScorer.EmitWaitStatWarnings) was never surfaced next to the row it belongs to. This ports PS's row layout (star name/duration, Auto bar/benefit, CharacterEllipsis + tooltip on the name and duration text) and adds the benefit column, joining each wait row to its "Wait: {type}" finding through a new pure helper, WaitRowText.Benefit, so the join and the formatting are pinned outside WPF. Not ported from PS: the theme-brush wait colors (tracked separately under #4578) and the header text's own benefit suffix (tracked under #4571) — this touches only the row layout inside ShowWaitStats. ## CHANGELOG entry SECTION: Changed ENTRY: The plan viewer's Wait Stats rows no longer clip their trailing benefit percentage in a narrow window, and now show one for every wait type the analyzer could score. ([#PR]) [#PR]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/PR
The wait-row benefit column (#4576) showed one decimal below 100%. PerformanceStudio's desktop plan viewer formats this value as a whole number, so PM now matches: 42.6 becomes "up to 43%", and a benefit below 1 rounds to "up to 0%" rather than showing a decimal. FormatBenefitPercent (used only by the plan-warning header, a different display) goes back to private.
# Conflicts: # PerformanceMonitor.Ui/PlanViewerControl.Properties.cs
erikdarlingdata
marked this pull request as ready for review
September 28, 2026 16:10
This was referenced Sep 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4576. Part of #4511.
Why
PerformanceStudio (erikdarlingdata/PerformanceStudio@78a3370, refined @80de6fc) fixed the Wait Stats card's rows so the flexible column is the duration text, not the bar; the wait-type name is capped at 150px with an ellipsis and a tooltip; and the trailing "up to N%" benefit sits in an Auto column so it is never the thing that gets clipped. Every row also carries a tooltip now.
PerformanceMonitor's wait grid (
PlanViewerControl.Properties.cs'sShowWaitStats, shared by the Darling Viewer and Lite) had none of this: a fixed name column sized off the longest wait-type string, a fixed 300px bar column, and no benefit column at all — the benefit percentage the analyzer already scores per wait type (BenefitScorer.EmitWaitStatWarnings) was never surfaced next to the row it belongs to.What changes
ShowWaitStats's grid columns are now*, Auto, *, Auto(name, bar, duration, benefit) instead of three fixed-width columns. The name and duration are the two star columns that ellipsize (TextTrimming.CharacterEllipsis) under pressure; the bar (60px) and benefit are Auto so neither is ever clipped.MaxWidth) with an ellipsis and a"{type} — {category} wait"tooltip."{ms} ms across {n} waits"tooltip."{total} ms of waits across {n} wait types"), cleared when a statement has no waits so a hover doesn't answer with a stale count.BenefitScorer.EmitWaitStatWarnings's "Wait: {type}" finding), with a"Up to N% of this statement's runtime could be recovered by removing {type} waits"tooltip. Absent when there's no matching finding, no score, or the score is zero.PerformanceMonitor.PlanAnalysis.WaitRowText.Benefit(waitType, statementWarnings)does the join and formatting (case-insensitive match on"Wait: {type}", reusingPlanWarningDisplay.FormatBenefitPercent's rounding, nowinternal). The WPF code-behind calls it; nothing else changed in the join.ShowWaitStats's signature grew aList<PlanWarning>parameter (the statement'sPlanWarnings); the one call site (PlanViewerControl.Rendering.cs) was updated.Lite: gets this too (shared control)
Lite hosts the same
PerformanceMonitor.Ui.PlanViewerControl, so this lands there with no separate change.Not ported / PM-ahead
ShowWaitStats.Test plan
New pin file:
Darling/Darling.Tests/ViewerWaitRowBenefitTests.cs— pinsWaitRowText.Benefit: a positive match, case-insensitive match, the shared 100%-and-above whole-number format, no matching finding, a null score, and a zero/negative score all resolving tonull.origin/dev(49c5b6a) fails with 7×CS0103: The name 'WaitRowText' does not exist in the current context— the type doesn't exist there yet."Wait: " + waitTypeto"Wait " + waitType(dropping the colon) — 3 of 7 tests failed (Benefit_ReturnsUpToNPercent_WhenAMatchingFindingHasAPositiveScore,Benefit_MatchesCaseInsensitively_OnTheWaitType,Benefit_UsesTheSharedWholeNumberFormat_AtAndAbove100). Reverted; rebuilt; all 7 green again.-class Darling.Tests.ViewerWaitRowBenefitTests:Total: 7, Errors: 0, Failed: 0, Skipped: 0.ViewerWaitRowBenefitTests+ShowPlanParserCondAndMultiplePlanTests,ActualPlanRequestTests,ActualPlanDispatchTests,ActualPlanResultParseTests,QueryModificationDetectorTests,ActualPlanCaptureLoopTests,ActualPlanGatingTests,ReproScriptBuilderHardeningTests,DarlingAnalysisPipelineTests,DarlingMcpPlanToolsSurfaceAndSqlTests,DarlingMcpPlanToolsLivePostgresTests,McpPlanAnalysisEnvelopeTests,SerialLoopStoreSizeSourceTests,TsqlConventionGuardTests,DocCommentHygieneTests):Total: 238, Errors: 0, Failed: 0, Skipped: 2(the two live-Postgres tests, gated onDARLING_TEST_PG, skip on this rig as expected).-p:EnableWindowsTargeting=true, analyzer-warnings grep empty on every log:Darling.Tests(Release),PerformanceMonitor.Ui,Lite/PerformanceMonitorLite.csproj,Lite.Tests,Darling/PerformanceMonitor.Darling.Viewer— all 0 warnings / 0 errors.PerformanceMonitor.PlanAnalysisitself builds 0/0 (theinternalvisibility change toFormatBenefitPercentdoesn't break anything outside the assembly; it's still only called from withinPerformanceMonitor.PlanAnalysis).Screenshot plan (pure layout, can't assert visually from here)
Open a captured plan with wait stats and at least one wait type the analyzer scored a benefit for (e.g. one with meaningful
CXPACKETorPAGEIOLATCH_SHtime), in both the Darling Viewer and Lite (shared control):HorizontalScrollBarVisibility="Disabled"onWaitStatsHeader'sScrollViewerwas already in place).Follow-up: the benefit text is now a whole percent (a13ed6e)
PerformanceStudio's desktop plan viewer formats the wait-row benefit as
$"up to {benefitPct:N0}%"— a whole number, no decimal.WaitRowText.Benefitwas formatting withPlanWarningDisplay.FormatBenefitPercent, which keeps one decimal below 100%. Fixed:WaitRowText.Benefitnow formats withN0directly, matching PerformanceStudio's rounding at every value, not just at and above 100%.FormatBenefitPercentgoes back toprivate(it's used only by the plan-warning header's own display, a different format).Pins updated in
ViewerWaitRowBenefitTests.cs: 42.6 → "up to 43%", 12.0 → "up to 12%", 100.0 → "up to 100%" (unchanged), and a new case, 0.4 → "up to 0%" (PerformanceStudio still shows the row when the score is positive but rounds to zero, since the> 0gating condition is on the raw score, not the rounded text).RED on the pre-fix commit (7fdbd52): copying the updated test file into a detached worktree at that commit and running
-class Darling.Tests.ViewerWaitRowBenefitTestsfails 3 of 8:Benefit_ReturnsUpToNPercent_WhenAMatchingFindingHasAPositiveScore("up to 43%" expected, "up to 42.6%" actual),Benefit_MatchesCaseInsensitively_OnTheWaitType" ("up to 12%" vs "up to 12.0%"), andBenefit_RoundsAFractionalScore_ToTheNearestWholePercent` ("up to 0%" vs "up to 0.4%").GREEN on the fix:
-class Darling.Tests.ViewerWaitRowBenefitTests→Total: 8, Errors: 0, Failed: 0, Skipped: 0.Census family, run on this rig:
CommentFilterAdoptionTests,DocCommentHygieneTests, and every*Census*Testsclass that doesn't need a local Postgres server (EntraProviderPackageCensusTests,FileGrowthRiseUnitCensusTests,McpPayloadContractCensusTests,McpServiceParameterDiSeatCensusTests,PgSettingScrubCandidateCensusTests,SameStatementPileupSourceCensusTests,WebExceptionTextCensusTests) all pass alongside the updated pin:Total: 285, Errors: 0, Failed: 38, Skipped: 0where every failure traces toLivePostgresStoreFixture.InitializeAsync(no Postgres server on this rig) — a pre-existing environment gap, not a regression from this change.DailyDeadlockWindowCensusTests,MeasurementContractCensusTests,PlanForceActionDetailCensusTests,RemoteCollectorServiceCancellationCensusTests,StoreApplicationNameCensusTests, andStoreSessionTimeZonePinCensusTestsweren't run separately for the same reason (they also need a live Postgres fixture); CI decides them.Builds,
-p:EnableWindowsTargeting=true, analyzer-warnings grep empty:Darling.Tests(Release) andPerformanceMonitor.Ui— both 0 warnings / 0 errors.CHANGELOG
SECTION: Changed
ENTRY: - The plan viewer's Wait Stats rows show each wait's potential benefit and never clip ([#4595]) - As in PerformanceStudio's desktop viewer, each wait row gains an "up to N%" benefit column taken from the plan's matching wait finding, the name and duration columns take the spare width with an ellipsis and a tooltip, and the bar and benefit columns size to their content so the percentage is never cut off.
REF: [#4595]: #4595