Do not throw on breakpoint event for a replaced breakpoint - #37
Conversation
SetBreakpoints deactivates a file's ICorDebugFunctionBreakpoint objects and drops the BreakpointInfo that owned them. A hit already in flight on one of those reaches HandleBreakpoint, FindByCorBreakpoint returns null, and ArgumentNullException.ThrowIfNull throws on the runtime event pump's thread. Continue instead, the way an unrecognised breakpoint type is handled a few lines above: the hit belongs to a breakpoint the client no longer has. Measured with a probe that re-sends a file's breakpoints every 20ms while the debuggee hits them, 15 second runs: 3-4 throws before, 0 after, with the replaced-breakpoint path taken 1-6 times instead.
|
Regarding the decompiles test - I bumped the decompiler version which changed the output file - clear your SharpIdeSymbolsCache, that should fix it. Added a comment at the failing assertion, because I noticed this too. |
|
@nevse The managed breakpoint change looks good. Regarding the dictionary change - I'm not convinced this is necessary - the dap request and event processing lock is intended to fix the error you are reporting. 0.1.9, the version you reproduced it on, was before this locking was introduced. Please reproduce this on main/0.1.12 Also, is your MCP server no longer open source? |
|
That was it — cleared the cache and the suite is 32 of 32. My diagnosis in the description was wrong, sorry: it was stale decompiler output on my side, not anything of yours. |
a67e64b to
4531dc4
Compare
|
You're right — dropped it. I hammered And yes, it's open source — https://github.com/nevse/SharpDbg.MCP. I just haven't pushed since the start of the month, so none of what I've been quoting is up there yet. |
|
@nevse please restore the footnote in the PR description, from the PR template |
|
Restored, and in #34 as well - I had dropped it in both. |
SetBreakpointsdeactivates the oldICorDebugFunctionBreakpoints and drops theBreakpointInfos that own them. A hit that was already in flight then finds nothing:Activate(false)cannot cancel a hit that has already happened — the process is frozen and the event is queued. The runtime callback does not takeDapRequestAndRuntimeEventLockeither, so draining the queue at the start of each request narrows the window without closing it: the event can land after the drain and before the replace.Every other "not our case" branch in that method logs and continues, including the one for an unrecognised breakpoint type a few lines above. This does the same.
Re-sending one file's breakpoints every 20ms for 15s while the debuggee hits them, over
SharpDbgInMemory:It is not a freeze on current main — the failed-handler recovery catches the throw and continues the process, so the session survives. What is left is an ordinary race being reported as an unexpected failure and relying on a last-resort path: that recovery only continues if
TryIsRunningreturnsS_OK, and it callsContinue()rather thanContinueWithVariableClear()like the rest of the method. On 0.1.9, before the recovery existed, the same hit froze the debuggee for good.No test: reproducing it needs the 20ms churn above, and a test built on that timing would be flaky. Happy to add one if you can see a deterministic way in.
Rebased onto 534170d,
SharpDbg.Cli.Tests32 of 32.(This PR originally also made
_modulesaConcurrentDictionary. Dropped — that could not be reproduced on 0.1.12, see the comments.)