-
Notifications
You must be signed in to change notification settings - Fork 119
Updated code so now we resolve pending breakpoints onScriptParsed instead onScriptPaused when using break on load #290
Conversation
Is there no guarantee that a scriptParsed event will come before a pause event on that script? Have you ever seen them come in the wrong order or is it just being careful?
That code is for the case where we set a BP and believe we have the correct sourcemapped location for it, but the script is not yet loaded. That exact check was added recently in c125224 and the bug was quite bad so the check should stay. But I suspect that when I fixed that, I actually broke something else which is what
I'm not sure, what do you mean by "refresh case" exactly? |
After discussing this with Dhanvi, we decided not to take this change. |
@roblourens: After discussing this in more depth, we decided to merge this, although we are not sure if we want to merge it for this release, or the next one. Please help us get this into a state where it's ready to merge, and after we get to that state we'll decide whether to merge it in this release, or in the next release |
Is there no guarantee that a scriptParsed event will come before a pause event on that script? Have you ever seen them come in the wrong order or is it just being careful? --> I'm assuming that you are asking why do we need this "waiting" logic. If that's the case, onScriptParsed and onScriptPaused do always arrive in the correct order. The issue is that we are not blocking the thread until all the work gets done. The resolved breakpoints are set in a non-blocking async manner, so it's possible that onScriptPaused executes while parts of onScriptParsed are still executing (in particular before we resolve all the breakpoints). |
I'm not sure, what do you mean by "refresh case" exactly? --> I'm not sure whether we need some special logic on clearTargetContext or something like that. What happens if we navigate or we refresh while we are in the middle of resolving breakpoints onScriptParsed and we navigate to the same page, or a different page? Can we have two onScriptParsed handlers for the same url been executed at the same time (one for before the refresh, one for after)? How do we handle things properly in that case? |
c8d0f70
to
f38ce75
Compare
@roblourens I've updated the PR to do something reasonable with bpSet, rather than just remove it :). |
src/chrome/breakOnLoadHelper.ts
Outdated
const anyBreakpointsAtPausedLocation = committedBps.filter(bp => | ||
bp.actualLocation.lineNumber === pausedLocation.lineNumber && bp.actualLocation.columnNumber === pausedLocation.columnNumber).length > 0; | ||
|
||
// If there were any pending breakpoints resolved and any of them was at (1,1) we shouldn't continue | ||
if (anyPendingBreakpointsResolved && anyBreakpointsAtPausedLocation) { | ||
// If there were any breakpoints at (1,1) we shouldn't continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"at (1,1)" is this still accurate? It can be anywhere now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// If there were any breakpoints at this location (Which generally should be (1,1)) we shouldn't continue
src/chrome/chromeDebugAdapter.ts
Outdated
|
||
if (script.url) { | ||
script.url = utils.fixDriveLetter(script.url); | ||
public breakpointsResolvedDefer(scriptId: string): PromiseDefer<void> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the verb in this method name, should it be a "get..."?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getBreakpointsResolvedDefer
src/chrome/chromeDebugAdapter.ts
Outdated
try { | ||
this.doAfterProcessingSourceEvents(async () => { // This will block future 'removed' source events, until this processing has been completed | ||
if (typeof this._columnBreakpointsEnabled === 'undefined') { | ||
await this.detectColumnBreakpointSupport(script.scriptId).then(async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't mix async/await and promises when possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (typeof this._columnBreakpointsEnabled === 'undefined') {
await this.detectColumnBreakpointSupport(script.scriptId);
await this.sendInitializedEvent();
}
src/chrome/chromeDebugAdapter.ts
Outdated
@@ -1316,7 +1341,7 @@ export abstract class ChromeDebugAdapter implements IDebugAdapter { | |||
}; | |||
} | |||
|
|||
private targetBreakpointResponsesToClientBreakpoints(url: string, responses: ISetBreakpointResult[], requestBps: DebugProtocol.SourceBreakpoint[], ids?: number[]): DebugProtocol.Breakpoint[] { | |||
private targetBreakpointResponsesToClientBreakpoints(url: string, responses: ISetBreakpointResult[], requestBps: DebugProtocol.SourceBreakpoint[], ids?: number[]): BreakpointSetResult[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
method name isn't accurate with this change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
targetBreakpointResponsesToBreakpointSetResults
src/chrome/chromeDebugAdapter.ts
Outdated
return undefined; | ||
} | ||
|
||
if (sources) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When is this undefined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (body.breakpoints.every(bp => !bp.verified)) { | ||
// If all breakpoints are set, we mark them as set. If not, we mark them as un-set so they'll be set | ||
const areAllSet = setBpResultBody.breakpoints.every(setBpResult => setBpResult.isSet); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this changes the way it works - bpsSet was true if the bp was attempted to be set - not that it was bound. The check below response.breakpointId !== undefined
is only true when the bp is bound.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding was that Debugger.setBreakpointByUrl always returns the breakpointId if the parameters are valid even if the breakpoint is unbound, and it also returns the actualLocation if the breakpoint was bound.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, you're correct.
I'm not sure refresh is an issue, because of This is a complicated change and I'm not sure I follow the whole thing. Take a look at the feedback and let's try to have a call about it in the next day or two. |
I'm adding some more awaits because if not the second call to breakpointsAreResolvedDefer.resolve() might be called to early... |
…tead onScriptPaused when using break on load
…lve() is called correctly
c1900ab
to
022bb8a
Compare
Please add some integration tests for this too |
src/chrome/chromeDebugAdapter.ts
Outdated
return <DebugProtocol.Breakpoint>{ | ||
// If we don't have an actualLocation nor a breakpointId this is a pseudo-breakpoint because we are using break-on-load | ||
// so we mark the breakpoint as not set, so i'll be set after we load the actual script that has the breakpoint | ||
return { isSet: response.breakpointId !== undefined, breakpoint: <DebugProtocol.Breakpoint>{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick, please format these like
{
isSet...
breakpoint: {
...
}
}
as I didn't notice there was a second object there at first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed
src/chrome/chromeDebugAdapter.ts
Outdated
this._pendingBreakpointsByUrl.delete(source); | ||
breakpointsAreResolvedDefer.resolve(); | ||
} else { | ||
breakpointsAreResolvedDefer.resolve(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just pull these two out of the if/else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't noticed that, thanks :)... It's actually a bug to do these here, so I'm removing both of them....
Never mind about the call. I think this will be fine. If you are comfortable with this, go ahead and merge it. |
src/chrome/chromeDebugAdapter.ts
Outdated
}); | ||
if (this._initialSourceMapsP) { | ||
this._initialSourceMapsP = <Promise<any>>Promise.all([this._initialSourceMapsP, sourceMapsP]); | ||
await sourceMapsP; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this always be awaited outside the if block? And why does it need to be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, it should be outside.
We want this so we don't call breakpointsAreResolvedDefer.resolve(); until this has finished for sure. If we call breakpointsAreResolvedDefer.resolve(); too early that will cause issues.
Left one more comment but you should have permission to merge it, please do that when you think it's ready. Let me know if it doesn't work, I'll do it. |
@roblourens Any more feedback? |
The general idea of this code change is that now all the pending breakpoints will get resolved onScriptParsed. Because we don't know in which order things will be executed, we'll have a defer used as a semaphore. The break-on-load logic inside onScriptPaused won't continue until the associated onScriptParsed has finished resolving all the pending breakpoints.
We need to make this change because of files that start with a function declaration, 0,0 is inside the function, so the break-on-load breakpoint is sometimes never hit, so then even after we refresh the page, no breakpoints are hit in that file because they are all still pending.
Is there a better way to solve this issue?
Some specific question:
I had to remove: "&& !pendingBP.bpsSet?" from line 697 to make this work. I'm not sure how that code was being used. It removing that code the right thing to do?
How should I handle the refresh case? Do we need to do anything special for that case? Should I call this._scriptIdToBreakpointsAreResolvedDefer.clear(); inside clearTargetContext?