You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CPU profiler drops a frame's file when its IL offset is on a hidden sequence point (F# match code) #18
FrameTable.Describe in dotnet/LspProfiler.DotNet.Engine/Symbolizer.cs keeps a managed frame's file only when TraceEvent reports a line above 0. F# compiles a match decision tree (active patterns, union cases) to hidden sequence points, and TraceEvent 3.2.6 reports those as line 0. Every sample in that code therefore arrives without a file, and FunctionKey(file, name) splits one function into a filed entry and a fileless one. This breaks [PROF-DOTNET-CPU-SYMBOLS], which resolves each managed frame to its PDB file:line.
Red reproduction
This runs the real engine (cpu --pid) against SharpLsp's src/sharplsp/tests/fixtures/fsharp-profiling/FsPricingEngine (Release, run parse) for 12 s on macOS arm64. The columns are samples whose stacks hold the function:
Function
With file
Without file
Csv.parseLine
1592
2507 (61%)
Csv.|Money|_|
1587
34
Csv.|SkuCode|_|
220
32
Csv.|Int|_|
401
30
parseLine (token 0x060000C5) has these sequence points in its portable PDB: 0:L31, hidden at 18, 20, 23, 29, 32, 33, 34, 49, 65, 82 and 86, then 129:L33, 150:L34 and a hidden point at 152. The sampled IL offsets 19, 46, 62, 78, 89 and 112 fall in that hidden run, and TraceEvent returns line 0 for each of them.
Effect
SharpLsp's Windows profiler chunk failed on Nimblesite/SharpLsp#406 (run 39364686929) with "the snapshot already names parseLine in Csv.fs", "parseAll: sampled 7.0%, measured 24.6%" and "shortestPath is hot in Routing.fs". The same job passed unchanged on the next run, so the split share decides pass or fail by timing.
Fix, prototyped outside the tree
When TraceEvent's line is 0, SequencePoint.HiddenLine or missing, take the line from the PDB instead:
the last visible sequence point at or before the IL offset;
the method's first visible point when there is none before it, as for a prolog (IL −1).
SourceLocator's ModuleSymbols cache already reads these PDBs, so the frame table can reuse it for the whole recording.
With the fix, parseLine has 3882 samples with its file and none without. Its hidden offsets resolve to line 31, the match. Methods that carry no debug information at all stay fileless: the closure Csv+parseAll@39.Invoke and Csv.get_invariant, whose MethodDebugInformation has no document.
Test
Put a hot F# match in HotLeakAppFs. Then assert in the e2e suite that every sampled frame of a method with visible sequence points carries its file and a line of its own.
Activity
Profiler: a CPU sample in hidden F# match code keeps its method's file (