From 694f425394923e9935c022e7d0995ed7c15c5a2b Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Fri, 26 Sep 2025 17:24:28 +0200 Subject: [PATCH 1/7] print stack guard thread jump counts --- src/Compiler/Facilities/DiagnosticsLogger.fs | 51 ++++++++++++++----- src/Compiler/Facilities/DiagnosticsLogger.fsi | 8 +-- .../src/FSharp.Editor/Common/DebugHelpers.fs | 2 + 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index eb96a5f6e0b..6f1edd493f6 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -16,6 +16,7 @@ open System.Runtime.InteropServices open Internal.Utilities.Library open Internal.Utilities.Library.Extras open System.Threading.Tasks +open System.Collections.Concurrent /// Represents the style being used to format errors [] @@ -868,6 +869,33 @@ let internal languageFeatureNotSupportedInLibraryError (langFeature: LanguageFea let suggestedVersionStr = LanguageVersion.GetFeatureVersionString langFeature error (Error(FSComp.SR.chkFeatureNotSupportedInLibrary (featureStr, suggestedVersionStr), m)) +module StackGuardMetrics = + open System + open System.Diagnostics.Metrics + + let meter = new Meter("FSharp.Compiler.DiagnosticsLogger.StackGuard", "1.0.0") + + let jumpCounter = meter.CreateCounter("stackguard-jumps", description = "Tracks the number of times the stack guard has jumped to a new thread") + + let jumps = ConcurrentDictionary() + + let Listen () = + let listener = new MeterListener() + + listener.EnableMeasurementEvents jumpCounter + + listener.SetMeasurementEventCallback(fun _ v tags _ -> + let memberName = nonNull tags[0].Value :?> string + let counter = jumps.GetOrAdd(memberName, fun _ -> ref 0L) + Interlocked.Add(counter, v) |> ignore) + + listener.Start() + listener :> IDisposable + + let StatsToString () = + let entries = jumps |> Seq.map (fun kvp -> $"{kvp.Key}: {kvp.Value.Value}") |> String.concat ", " + $"StackGuard jumps: {entries} \n" + /// Guard against depth of expression nesting, by moving to new stack when a maximum depth is reached type StackGuard(maxDepth: int, name: string) = @@ -877,27 +905,22 @@ type StackGuard(maxDepth: int, name: string) = member _.Guard ( f, - [] memberName: string, - [] path: string, - [] line: int + [] memberName: string ) = - Activity.addEventWithTags - "DiagnosticsLogger.StackGuard.Guard" - (seq { - Activity.Tags.stackGuardName, box name - Activity.Tags.stackGuardCurrentDepth, depth - Activity.Tags.stackGuardMaxDepth, maxDepth - Activity.Tags.callerMemberName, memberName - Activity.Tags.callerFilePath, path - Activity.Tags.callerLineNumber, line - }) - depth <- depth + 1 try if depth % maxDepth = 0 then + let tags = + let mutable tags = TagList() + tags.Add("caller", memberName) + tags + + + StackGuardMetrics.jumpCounter.Add(1L, &tags) + async { do! Async.SwitchToNewThread() Thread.CurrentThread.Name <- $"F# Extra Compilation Thread for {name} (depth {depth})" diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fsi b/src/Compiler/Facilities/DiagnosticsLogger.fsi index 02471dd383b..b2033968f84 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fsi +++ b/src/Compiler/Facilities/DiagnosticsLogger.fsi @@ -459,15 +459,17 @@ val tryLanguageFeatureErrorOption: val languageFeatureNotSupportedInLibraryError: langFeature: LanguageFeature -> m: range -> 'T +module StackGuardMetrics = + val Listen: unit -> IDisposable + val StatsToString: unit -> string + type StackGuard = new: maxDepth: int * name: string -> StackGuard /// Execute the new function, on a new thread if necessary member Guard: f: (unit -> 'T) * - [] memberName: string * - [] path: string * - [] line: int -> + [] memberName: string -> 'T member GuardCancellable: Internal.Utilities.Library.Cancellable<'T> -> Internal.Utilities.Library.Cancellable<'T> diff --git a/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs b/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs index 51cdd83f8fa..b06461982c5 100644 --- a/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs @@ -123,10 +123,12 @@ module FSharpServiceTelemetry = let periodicallyDisplayCacheStats = cancellableTask { use _ = CacheMetrics.ListenToAll() + use _ = FSharp.Compiler.DiagnosticsLogger.StackGuardMetrics.Listen() while true do do! Task.Delay(TimeSpan.FromSeconds 10.0) FSharpOutputPane.logMsg (CacheMetrics.StatsToString()) + FSharpOutputPane.logMsg (FSharp.Compiler.DiagnosticsLogger.StackGuardMetrics.StatsToString()) } #if DEBUG From 7b7a750d1d3ff69aba6577b0a533596c43c545de Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Sat, 27 Sep 2025 10:53:29 +0200 Subject: [PATCH 2/7] print with --times --- src/Compiler/Driver/fsc.fs | 1 + src/Compiler/Facilities/DiagnosticsLogger.fs | 56 ++++++++++++------- src/Compiler/Facilities/DiagnosticsLogger.fsi | 6 +- src/Compiler/Utilities/Activity.fs | 3 + src/Compiler/Utilities/Activity.fsi | 5 +- src/Compiler/Utilities/Caches.fs | 2 +- tests/FSharp.Test.Utilities/XunitHelpers.fs | 2 +- 7 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index 2bea066e427..89f196c810f 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -569,6 +569,7 @@ let main1 exiter.Exit 1 if tcConfig.showTimes then + StackGuardMetrics.CaptureStatsAndWriteToConsole() |> disposables.Register Caches.CacheMetrics.CaptureStatsAndWriteToConsole() |> disposables.Register Activity.Profiling.addConsoleListener () |> disposables.Register diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 6f1edd493f6..945b0752a1b 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -870,14 +870,26 @@ let internal languageFeatureNotSupportedInLibraryError (langFeature: LanguageFea error (Error(FSComp.SR.chkFeatureNotSupportedInLibrary (featureStr, suggestedVersionStr), m)) module StackGuardMetrics = - open System open System.Diagnostics.Metrics - let meter = new Meter("FSharp.Compiler.DiagnosticsLogger.StackGuard", "1.0.0") + let meter = FSharp.Compiler.Diagnostics.Metrics.Meter - let jumpCounter = meter.CreateCounter("stackguard-jumps", description = "Tracks the number of times the stack guard has jumped to a new thread") + let jumpCounter = + meter.CreateCounter( + "stackguard-jumps", + description = "Tracks the number of times the stack guard has jumped to a new thread" + ) - let jumps = ConcurrentDictionary() + let countJump memberName = + let tags = + let mutable tags = TagList() + tags.Add("caller", memberName) + tags + + jumpCounter.Add(1L, &tags) + + // Used by the self-listener. + let jumpsByFunctionName = ConcurrentDictionary() let Listen () = let listener = new MeterListener() @@ -886,15 +898,31 @@ module StackGuardMetrics = listener.SetMeasurementEventCallback(fun _ v tags _ -> let memberName = nonNull tags[0].Value :?> string - let counter = jumps.GetOrAdd(memberName, fun _ -> ref 0L) + let counter = jumpsByFunctionName.GetOrAdd(memberName, fun _ -> ref 0L) Interlocked.Add(counter, v) |> ignore) listener.Start() listener :> IDisposable let StatsToString () = - let entries = jumps |> Seq.map (fun kvp -> $"{kvp.Key}: {kvp.Value.Value}") |> String.concat ", " - $"StackGuard jumps: {entries} \n" + let entries = + jumpsByFunctionName + |> Seq.map (fun kvp -> $"{kvp.Key}: {kvp.Value.Value}") + |> String.concat ", " + + if entries.Length > 0 then + $"StackGuard jumps: {entries} \n" + else + "" + + let CaptureStatsAndWriteToConsole () = + let listener = Listen() + + { new IDisposable with + member _.Dispose() = + listener.Dispose() + StatsToString() |> printfn "%s" + } /// Guard against depth of expression nesting, by moving to new stack when a maximum depth is reached type StackGuard(maxDepth: int, name: string) = @@ -902,24 +930,14 @@ type StackGuard(maxDepth: int, name: string) = let mutable depth = 1 [] - member _.Guard - ( - f, - [] memberName: string - ) = + member _.Guard(f, [] memberName: string) = depth <- depth + 1 try if depth % maxDepth = 0 then - let tags = - let mutable tags = TagList() - tags.Add("caller", memberName) - tags - - - StackGuardMetrics.jumpCounter.Add(1L, &tags) + StackGuardMetrics.countJump memberName async { do! Async.SwitchToNewThread() diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fsi b/src/Compiler/Facilities/DiagnosticsLogger.fsi index b2033968f84..f81b24cec45 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fsi +++ b/src/Compiler/Facilities/DiagnosticsLogger.fsi @@ -462,15 +462,13 @@ val languageFeatureNotSupportedInLibraryError: langFeature: LanguageFeature -> m module StackGuardMetrics = val Listen: unit -> IDisposable val StatsToString: unit -> string + val CaptureStatsAndWriteToConsole: unit -> IDisposable type StackGuard = new: maxDepth: int * name: string -> StackGuard /// Execute the new function, on a new thread if necessary - member Guard: - f: (unit -> 'T) * - [] memberName: string -> - 'T + member Guard: f: (unit -> 'T) * [] memberName: string -> 'T member GuardCancellable: Internal.Utilities.Library.Cancellable<'T> -> Internal.Utilities.Library.Cancellable<'T> diff --git a/src/Compiler/Utilities/Activity.fs b/src/Compiler/Utilities/Activity.fs index 818d22e6f81..1712d89d6d6 100644 --- a/src/Compiler/Utilities/Activity.fs +++ b/src/Compiler/Utilities/Activity.fs @@ -18,6 +18,9 @@ module ActivityNames = let AllRelevantNames = [| FscSourceName; ProfiledSourceName |] +module Metrics = + let Meter = new Metrics.Meter(ActivityNames.FscSourceName, "1.0.0") + [] module internal Activity = diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index 83d4b2772ec..b6381a9a47d 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -2,7 +2,7 @@ namespace FSharp.Compiler.Diagnostics open System -open Internal.Utilities.Library +open System.Diagnostics.Metrics /// For activities following the dotnet distributed tracing concept /// https://learn.microsoft.com/dotnet/core/diagnostics/distributed-tracing-concepts?source=recommendations @@ -16,6 +16,9 @@ module ActivityNames = val AllRelevantNames: string[] +module Metrics = + val Meter: Meter + /// For activities following the dotnet distributed tracing concept /// https://learn.microsoft.com/dotnet/core/diagnostics/distributed-tracing-concepts?source=recommendations [] diff --git a/src/Compiler/Utilities/Caches.fs b/src/Compiler/Utilities/Caches.fs index aedc7f7b8e4..21687ea2d89 100644 --- a/src/Compiler/Utilities/Caches.fs +++ b/src/Compiler/Utilities/Caches.fs @@ -10,7 +10,7 @@ open System.Diagnostics.Metrics open System.IO module CacheMetrics = - let Meter = new Meter("FSharp.Compiler.Cache") + let Meter = FSharp.Compiler.Diagnostics.Metrics.Meter let adds = Meter.CreateCounter("adds", "count") let updates = Meter.CreateCounter("updates", "count") let hits = Meter.CreateCounter("hits", "count") diff --git a/tests/FSharp.Test.Utilities/XunitHelpers.fs b/tests/FSharp.Test.Utilities/XunitHelpers.fs index 5365a243946..7e907492e65 100644 --- a/tests/FSharp.Test.Utilities/XunitHelpers.fs +++ b/tests/FSharp.Test.Utilities/XunitHelpers.fs @@ -175,7 +175,7 @@ type OpenTelemetryExport(testRunName, enable) = // Configure OpenTelemetry metrics export. Metrics can be viewed in Prometheus or other compatible tools. OpenTelemetry.Sdk.CreateMeterProviderBuilder() - .AddMeter(CacheMetrics.Meter.Name) + .AddMeter(ActivityNames.FscSourceName) .AddMeter("System.Runtime") .ConfigureResource(fun r -> r.AddService(testRunName) |> ignore) .AddOtlpExporter(fun e m -> From 13a29eead3d8aa5e4a7d46c8762189ae6addbb7e Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Sat, 27 Sep 2025 11:15:01 +0200 Subject: [PATCH 3/7] use known tag name --- src/Compiler/Facilities/DiagnosticsLogger.fs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 945b0752a1b..2680b20c0f6 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -870,7 +870,6 @@ let internal languageFeatureNotSupportedInLibraryError (langFeature: LanguageFea error (Error(FSComp.SR.chkFeatureNotSupportedInLibrary (featureStr, suggestedVersionStr), m)) module StackGuardMetrics = - open System.Diagnostics.Metrics let meter = FSharp.Compiler.Diagnostics.Metrics.Meter @@ -883,7 +882,7 @@ module StackGuardMetrics = let countJump memberName = let tags = let mutable tags = TagList() - tags.Add("caller", memberName) + tags.Add(Activity.Tags.callerMemberName, memberName) tags jumpCounter.Add(1L, &tags) @@ -892,7 +891,7 @@ module StackGuardMetrics = let jumpsByFunctionName = ConcurrentDictionary() let Listen () = - let listener = new MeterListener() + let listener = new Metrics.MeterListener() listener.EnableMeasurementEvents jumpCounter From 78fc7e93d44d969dbc23f937bd8c0f7c984bdeb7 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Sat, 27 Sep 2025 12:00:42 +0200 Subject: [PATCH 4/7] internal --- src/Compiler/Facilities/DiagnosticsLogger.fsi | 2 +- src/Compiler/Utilities/Activity.fsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fsi b/src/Compiler/Facilities/DiagnosticsLogger.fsi index f81b24cec45..16dfdf64882 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fsi +++ b/src/Compiler/Facilities/DiagnosticsLogger.fsi @@ -459,7 +459,7 @@ val tryLanguageFeatureErrorOption: val languageFeatureNotSupportedInLibraryError: langFeature: LanguageFeature -> m: range -> 'T -module StackGuardMetrics = +module internal StackGuardMetrics = val Listen: unit -> IDisposable val StatsToString: unit -> string val CaptureStatsAndWriteToConsole: unit -> IDisposable diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index b6381a9a47d..50d134d559e 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -16,7 +16,7 @@ module ActivityNames = val AllRelevantNames: string[] -module Metrics = +module internal Metrics = val Meter: Meter /// For activities following the dotnet distributed tracing concept From 63799b556e622c8a4378215d5ab6f201eef04f39 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:01:26 +0200 Subject: [PATCH 5/7] extract table printing to module --- src/Compiler/Facilities/DiagnosticsLogger.fs | 29 +++++++--- src/Compiler/Facilities/DiagnosticsLogger.fsi | 7 ++- src/Compiler/Utilities/Activity.fs | 45 +++++++++++++++ src/Compiler/Utilities/Activity.fsi | 2 + src/Compiler/Utilities/Caches.fs | 55 ++++++------------- 5 files changed, 91 insertions(+), 47 deletions(-) diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 2680b20c0f6..338c2ec48fd 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -904,15 +904,18 @@ module StackGuardMetrics = listener :> IDisposable let StatsToString () = - let entries = - jumpsByFunctionName - |> Seq.map (fun kvp -> $"{kvp.Key}: {kvp.Value.Value}") - |> String.concat ", " + let headers = [ "Caller"; "Jumps" ] - if entries.Length > 0 then - $"StackGuard jumps: {entries} \n" - else + let data = + [ + for kvp in jumpsByFunctionName do + [ kvp.Key; string kvp.Value.Value ] + ] + + if List.isEmpty data then "" + else + $"StackGuard jumps:\n{Metrics.printTable headers data}" let CaptureStatsAndWriteToConsole () = let listener = Listen() @@ -929,14 +932,22 @@ type StackGuard(maxDepth: int, name: string) = let mutable depth = 1 [] - member _.Guard(f, [] memberName: string) = + member _.Guard + ( + f, + [] memberName: string, + [] path: string, + [] line: int + ) = depth <- depth + 1 try if depth % maxDepth = 0 then - StackGuardMetrics.countJump memberName + let fileName = System.IO.Path.GetFileName(path) + + StackGuardMetrics.countJump $"{memberName} ({fileName}:{line})" async { do! Async.SwitchToNewThread() diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fsi b/src/Compiler/Facilities/DiagnosticsLogger.fsi index 16dfdf64882..7bef3d3a15a 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fsi +++ b/src/Compiler/Facilities/DiagnosticsLogger.fsi @@ -468,7 +468,12 @@ type StackGuard = new: maxDepth: int * name: string -> StackGuard /// Execute the new function, on a new thread if necessary - member Guard: f: (unit -> 'T) * [] memberName: string -> 'T + member Guard: + f: (unit -> 'T) * + [] memberName: string * + [] path: string * + [] line: int -> + 'T member GuardCancellable: Internal.Utilities.Library.Cancellable<'T> -> Internal.Utilities.Library.Cancellable<'T> diff --git a/src/Compiler/Utilities/Activity.fs b/src/Compiler/Utilities/Activity.fs index 1712d89d6d6..c46ca661995 100644 --- a/src/Compiler/Utilities/Activity.fs +++ b/src/Compiler/Utilities/Activity.fs @@ -21,6 +21,51 @@ module ActivityNames = module Metrics = let Meter = new Metrics.Meter(ActivityNames.FscSourceName, "1.0.0") + let formatTable headers rows = + let columnWidths = + headers :: rows + |> List.transpose + |> List.map (List.map String.length >> List.max) + + let center width (cell: string) = + String.replicate ((width - cell.Length) / 2) " " + cell |> _.PadRight(width) + + let headers = (columnWidths, headers) ||> List.map2 center + + let printRow (row: string list) = + row + |> List.mapi (fun i (cell: string) -> + if i = 0 then + cell.PadRight(columnWidths[i]) + else + cell.PadLeft(columnWidths[i])) + |> String.concat " | " + |> sprintf "| %s |" + + let headerRow = printRow headers + + let divider = headerRow |> String.map (fun c -> if c = '|' then c else '-') + let hl = String.replicate divider.Length "-" + + use sw = new StringWriter() + + sw.WriteLine hl + sw.WriteLine headerRow + sw.WriteLine divider + + for row in rows do + sw.WriteLine(printRow row) + + sw.WriteLine hl + + string sw + + let printTable headers rows = + try + formatTable headers rows + with exn -> + $"Error formatting table: {exn}" + [] module internal Activity = diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index 50d134d559e..8ff0a4c3494 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -19,6 +19,8 @@ module ActivityNames = module internal Metrics = val Meter: Meter + val printTable: headers: string list -> rows: string list list -> string + /// For activities following the dotnet distributed tracing concept /// https://learn.microsoft.com/dotnet/core/diagnostics/distributed-tracing-concepts?source=recommendations [] diff --git a/src/Compiler/Utilities/Caches.fs b/src/Compiler/Utilities/Caches.fs index 21687ea2d89..cae35a93015 100644 --- a/src/Compiler/Utilities/Caches.fs +++ b/src/Compiler/Utilities/Caches.fs @@ -96,43 +96,24 @@ module CacheMetrics = listener :> IDisposable let StatsToString () = - use sw = new StringWriter() - - let nameColumnWidth = - [ yield! statsByName.Keys; "Cache name" ] |> Seq.map String.length |> Seq.max - - let columns = allCounters |> List.map _.Name - let columnWidths = columns |> List.map String.length |> List.map (max 8) - - let header = - "| " - + String.concat - " | " - [ - "Cache name".PadRight nameColumnWidth - "hit-ratio" - for w, c in (columnWidths, columns) ||> List.zip do - $"{c.PadLeft w}" - ] - + " |" - - sw.WriteLine(String('-', header.Length)) - sw.WriteLine(header) - sw.WriteLine(header |> String.map (fun c -> if c = '|' then '|' else '-')) - - for kv in statsByName do - let name = kv.Key - let stats = kv.Value - let totals = stats.GetTotals() - sw.Write $"| {name.PadLeft nameColumnWidth} | {stats.Ratio, 9:P2} |" - - for w, c in (columnWidths, columns) ||> List.zip do - sw.Write $" {totals[c].ToString().PadLeft(w)} |" - - sw.WriteLine() - - sw.WriteLine(String('-', header.Length)) - string sw + let headers = [ "Cache name"; "hit-ratio" ] @ (allCounters |> List.map _.Name) + + let rows = + [ + for kv in statsByName do + let name = kv.Key + let stats = kv.Value + let totals = stats.GetTotals() + + [ + yield name + yield $"{stats.Ratio:P2}" + for c in allCounters do + yield $"{totals[c.Name]}" + ] + ] + + FSharp.Compiler.Diagnostics.Metrics.printTable headers rows let CaptureStatsAndWriteToConsole () = let listener = ListenToAll() From b6b160efb2495e02061e7a471940efdf8309a863 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:03:01 +0200 Subject: [PATCH 6/7] remove meter version --- src/Compiler/Utilities/Activity.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Utilities/Activity.fs b/src/Compiler/Utilities/Activity.fs index c46ca661995..1995e94ad6b 100644 --- a/src/Compiler/Utilities/Activity.fs +++ b/src/Compiler/Utilities/Activity.fs @@ -19,7 +19,7 @@ module ActivityNames = let AllRelevantNames = [| FscSourceName; ProfiledSourceName |] module Metrics = - let Meter = new Metrics.Meter(ActivityNames.FscSourceName, "1.0.0") + let Meter = new Metrics.Meter(ActivityNames.FscSourceName) let formatTable headers rows = let columnWidths = From 108cf243e7c8d36b0dbd6732a4a678eefe0be56f Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Mon, 29 Sep 2025 22:09:58 +0200 Subject: [PATCH 7/7] separate column for source location --- src/Compiler/Facilities/DiagnosticsLogger.fs | 15 +++++++++------ .../src/FSharp.Editor/Common/DebugHelpers.fs | 2 +- .../LanguageService/LanguageService.fs | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 338c2ec48fd..2a087283468 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -879,16 +879,17 @@ module StackGuardMetrics = description = "Tracks the number of times the stack guard has jumped to a new thread" ) - let countJump memberName = + let countJump memberName location = let tags = let mutable tags = TagList() tags.Add(Activity.Tags.callerMemberName, memberName) + tags.Add("source", location) tags jumpCounter.Add(1L, &tags) // Used by the self-listener. - let jumpsByFunctionName = ConcurrentDictionary() + let jumpsByFunctionName = ConcurrentDictionary<_, int64 ref>() let Listen () = let listener = new Metrics.MeterListener() @@ -897,19 +898,21 @@ module StackGuardMetrics = listener.SetMeasurementEventCallback(fun _ v tags _ -> let memberName = nonNull tags[0].Value :?> string - let counter = jumpsByFunctionName.GetOrAdd(memberName, fun _ -> ref 0L) + let source = nonNull tags[1].Value :?> string + let counter = jumpsByFunctionName.GetOrAdd((memberName, source), fun _ -> ref 0L) Interlocked.Add(counter, v) |> ignore) listener.Start() listener :> IDisposable let StatsToString () = - let headers = [ "Caller"; "Jumps" ] + let headers = [ "caller"; "source"; "jumps" ] let data = [ for kvp in jumpsByFunctionName do - [ kvp.Key; string kvp.Value.Value ] + let (memberName, source) = kvp.Key + [ memberName; source; string kvp.Value.Value ] ] if List.isEmpty data then @@ -947,7 +950,7 @@ type StackGuard(maxDepth: int, name: string) = let fileName = System.IO.Path.GetFileName(path) - StackGuardMetrics.countJump $"{memberName} ({fileName}:{line})" + StackGuardMetrics.countJump memberName $"{fileName}:{line}" async { do! Async.SwitchToNewThread() diff --git a/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs b/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs index b06461982c5..fa8303b7e60 100644 --- a/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/DebugHelpers.fs @@ -120,7 +120,7 @@ module FSharpServiceTelemetry = ActivitySource.AddActivityListener(listener) - let periodicallyDisplayCacheStats = + let periodicallyDisplayMetrics = cancellableTask { use _ = CacheMetrics.ListenToAll() use _ = FSharp.Compiler.DiagnosticsLogger.StackGuardMetrics.Listen() diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 92b6979883a..6c84d3fb810 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -417,7 +417,7 @@ type internal FSharpPackage() as this = false, fun _ _ -> task { - DebugHelpers.FSharpServiceTelemetry.periodicallyDisplayCacheStats + DebugHelpers.FSharpServiceTelemetry.periodicallyDisplayMetrics |> CancellableTask.start this.DisposalToken |> ignore }