Repository navigation
Progress operation log: never truncate within a host's run, trim only whole operations - #16
Merged
Merged
Conversation
An array or a framework type has no source location ([PROF-DOTNET-SOURCE]), and the spec keeps such a site in the UI with no diagnostic. The wire adapter turned its absent file and line into "" and line 0, so the dashboard listed a 0th line and decorations tried to paint a file named "". MemoryAllocation's file and line are now optional: an unlocated site lists by name and paints no line.
The objects step summarised only the twenty largest types, so a type too small to rank (an F# union case such as OrderStatus+Paid, or the one list that holds a leak) had no row agreeing with its totals. The summary now appends that type's row after the ranked ones, keeping largest-first order.
Each test crate compiles tests/support on its own, and only the F# suite drives a fixture's line protocol, so Fixture::send and the stdout reader it reads were dead code in the C# suite's crate, which denies dead_code. Both now carry a documented per-item allow.
The fixture serialised its workload and its line protocol with one lock wrapped around Hot.spin, so the CPU profile no longer put Hot.spin on the busy thread's stack for most ticks and the F# CPU suite failed. The workload now runs lock-free and flags its own busy state, and idle work keeps allocating so a snapshot's induced collection can run. The FSharp.Core anchor test reads the order count from the reply, whatever its prefix.
…to targets The retention graph ignored is_root: decodeRefGraph dropped it, and the canvas coloured nodes by depth and drew only their types. A root's label, such as an F# module value's `static sessions`, never reached the screen, and roots were not distinct, against [PROF-UI-REFGRAPH]. The random force layout also turned every graph with many targets into an unreadable knot. Roots now survive decoding, are drawn in their own colour and named by their label above their type. Rows follow each node's depth: roots on top, targets along the bottom, and a row too tight to label names its type once with a count.
toProfileResult turned a frame the runtime could not place, such as System.Threading.Thread.StartCallback() with no framework PDB here, into file "" and line 0, which the results table rendered as a :0 link to nowhere. The location is now both fields or neither, shared with the memory sites; the heat map paints only located functions and the table lists the rest by name.
A `duration` on start capped the recorded time, but the session stayed in `cpu.list` and kept sending progress until someone stopped it, so a timed session never ended by itself ([PROF-CPU-SESSIONS]). Once its duration has elapsed, a session is no longer listed as active and its progress ends; its recording is still kept for the next `stop`.
Stop cleared the UI's tracking state and decorations but never sent `memory.stop`, so the server kept the session and its heap graphs, and answered snapshots for a session the user had stopped. Stop now ends the server session before the UI forgets its id, and a direct runtime whose program ended ends its session too.
…e peak The CPU session enabled the runtime provider beside the sampler. While any session enables it, the runtime keeps its list of the types it has already logged, so every heap snapshot taken during a CPU session after the first reported the types an earlier snapshot named as bare addresses. The CPU session now enables only the sampler; each segment's rundown still names its frames. peak_bytes was each snapshot's own heap size, so a release and a collection lowered it. The backend now keeps the session's peak, as the wire defines it. The F# engine suite becomes a module test crate under 500 lines a file, clippy-clean, with the line-protocol zoo tests and failing-first tests for both fixes.
The log trimmed single entries once it passed 500. A host runs all its suites in one extension host, and SharpLsp's profiler run now records more than that, so the log's length froze at the cap (slice(before) saw nothing of a refresh) and a trimmed begin: left its end: orphaned. The cap is now 10,000, far above a whole run, and an overflow trims back to half of it only where no operation is open.
MelbourneDeveloper
added a commit
to Nimblesite/SharpLsp
that referenced
this pull request
Oct 18, 2026
The profiler UI's progress operation log trimmed single entries past 500, which SharpLsp's profiler run now exceeds, so the refresh-progress and balanced-progress checks read a truncated log.
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.
TLDR
The progress operation log, the e2e seam hosts read, stops truncating partway through a host's test run, so its length stays a cursor and every
end:keeps itsbegin:.Details
progress-ops.tstrimmed single entries off the front once the log passed 500. A host runs all its suites in one extension host, and SharpLsp's profiler run now records more than 500 operations. The log's length then froze at the cap, sorecordedOperations().slice(before)saw nothing of a refresh it had just recorded, and a trimmedbegin:left itsend:orphaned, so the log claimed a notification closed that never opened.OPERATION_LOG_CAPis now 10,000 entries (a few hundred KB at most), far above a whole host run. An overflow trims back to half the cap, and only at a boundary where no operation is open (settledPrefix), so the log is never unbalanced.How Do The Automated Tests Prove It Works?
progress-ops.test.ts, "every operation of a long run stays recorded, balanced, with its length a cursor", records 600 operations, each with a step. It asserts that all 1,800 entries are kept, that the first operation is still present, thatslice(before)after one more operation is exactly its begin and end, and that begins equal ends. Before the fix it failed with "begin, step and end of every operation are kept".