Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.

[Breakpoints] + [SourceMaps] breakpoints are out of sync with the server on reload #3039

Merged
jasonLaster merged 9 commits into
firefox-devtools:masterfrom
codehag:bug/2904-breakpoints-sourcemaps-breakpoints-are-out-of-sync-with-the-server-on-reload
Jun 5, 2017
Merged

[Breakpoints] + [SourceMaps] breakpoints are out of sync with the server on reload #3039
jasonLaster merged 9 commits into
firefox-devtools:masterfrom
codehag:bug/2904-breakpoints-sourcemaps-breakpoints-are-out-of-sync-with-the-server-on-reload

Conversation

@codehag
Copy link
Copy Markdown
Contributor

@codehag codehag commented May 30, 2017

Associated Issue: #2904

WIP

@codehag codehag force-pushed the bug/2904-breakpoints-sourcemaps-breakpoints-are-out-of-sync-with-the-server-on-reload branch 2 times, most recently from 75ea4ed to 4853833 Compare May 30, 2017 17:41
@codecov
Copy link
Copy Markdown

codecov Bot commented May 30, 2017

Codecov Report

Merging #3039 into master will increase coverage by 0.41%.
The diff coverage is 88.23%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3039      +/-   ##
==========================================
+ Coverage   66.15%   66.56%   +0.41%     
==========================================
  Files          74       75       +1     
  Lines        2603     2668      +65     
  Branches      527      541      +14     
==========================================
+ Hits         1722     1776      +54     
- Misses        881      892      +11
Impacted Files Coverage Δ
src/utils/utils.js 18.18% <ø> (-6.82%) ⬇️
src/client/firefox/commands.js 19.58% <0%> (-0.42%) ⬇️
src/actions/sources.js 51.26% <100%> (-0.61%) ⬇️
src/utils/prefs.js 92.3% <60%> (-7.7%) ⬇️
src/utils/breakpoint/index.js 91.66% <91.66%> (ø)
src/reducers/breakpoints.js 89.03% <92.15%> (-1.55%) ⬇️
src/actions/breakpoints.js 88.04% <96.77%> (+2.12%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9d582f4...219286c. Read the comment docs.

Comment thread src/actions/breakpoints.js Outdated

const breakpoint = _createBreakpoint(location, referenceBreakpoint);

let promise = addClientBreakpoint(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused, so I'll walk through the bug and two workflows as I see them. I could be missing something, it could be what you're already doing too. I'll outline a simple case and syncing case for both workflows. To be clear, we can do the simple case and fix the bug.

Bug

  1. User is working on an app that uses source maps (i.e. namely bundle.js w/ file a.js, b.js, c.js)
  2. User debugs the app and adds a bp to b.js line 12, with the generated location bundle.js#45
  3. User adds code to a.js
  4. User reloads the app and see the debugger paused on b.js line 4, which happens to line up with the generated location bundle.js#45!

We will fix this, either simply moving the breakpoint to b.js line 4 when the debugger reloads or ideally re-removing the breakpoint at line bundle.js#45 and adding a breakpoint at the new location...


simple case

  1. User adds a Breakpoint
  • addBreakpoint is called with an original location
  • generated location is calculated based on the source's sourcemap
  • a pending breakpoint is added with the generated location
  1. User reloads page / User re-opens debugger
  • breakpoints reducer adds pending breakpoints
  • newSource is called and the debugger attempts to sync the breakpoint
    • checks if there is a pending breakpoint
    • uses the pending bps generated location to recalculate the original location
    • adds a new breakpoint to the store

syncing case

  1. User adds a Breakpoint
  • addBreakpoint is called with an original location
  • generated location is calculated based on the source's sourcemap
  • a pending breakpoint is added with the generated location
  1. User reloads page / User re-opens debugger
  • breakpoints reducer adds pending breakpoints
  • newSource is called and the debugger attempts to sync the breakpoint
    • checks if there is a pending breakpoint
    • uses the pending bps generated location to recalculate the original location
    • if the new original location is different from the pending breakpoint original location
      • removes the old breakpoint on the server
      • adds the new breakpoint to the server
    • adds the new breakpoint to the store

Comment thread src/actions/breakpoints.js Outdated
) {
console.log("IM CALLED NOW");
const location = breakpoint.location;
await client.removeBreakpoint(referenceBreakpoint.id);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do we know we have an ID? the pending bp doesn't have an id

Copy link
Copy Markdown
Contributor

@jasonLaster jasonLaster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really great work. When the dust settles we'll have a simpler system.

I left lots of comments to over share.

import { updateObj } from "../utils";
// Return the first argument that is a string, or null if nothing is a
// string.
export function firstString(...args) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be moved to utils/utils.js

return location;
}
return Object.assign({}, location, { column: undefined });
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 things:

  1. I think we should do this in firefox/commands.js#onNewBreakpoint
  2. onNewBreakpoint should be moved to firefox/create#createBreakpoint

The rationale for this is to give us consistent data from the server... we did the same thing for source URLs when we worked on pending bps

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

return await sourceMaps.getGeneratedLocation(location, source.toJS());
}

async function syncClientBreakpoint(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add a function comment, we don't typically have comments but we should lol

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor style nit, but i move actiony things to options i.e.

syncClientBreakpoint(source, pendingBreakpoint, location, {client, sourceMaps})

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we now have just 4 items, so this should be ok

Comment thread src/actions/breakpoints.js Outdated

const originalLocation = await sourceMaps.getOriginalLocation(
Object.assign({}, generatedLocation, {
line: pendingBreakpoint.generatedLocation.line
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems weird to me... can you explain the rationale

Copy link
Copy Markdown
Contributor Author

@codehag codehag Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. and i agree that this needs to be expressed better. I didn't quite finish fixing this

when the source is reloaded, the source id changes, and i was confused by that. I wasn't sure what the source was so I quickly threw this in to see if it would work. Basically, there is a new connection. This is why we can't delete / find the line after the debuggee is reloaded.

for example,

pendingBreakpoint.generatedLocation
/* { 
  id: "server1.conn3.child1/34",  
  line: 51234
} */
generatedLocation // 
/* { 
  id: "server1.conn4.child1/34",  
  line: 51235
} */

I didn't get around to doing this properly, but we now have the source available in this function, and this should actually be:

const originalLocation = await sourceMaps.getOriginalLocation({
  id: source.id,
  line: pendingBreakpoint.generatedLocation.line
});

Also, i don't think we need to save the generated location in full, just the line. will keep hacking away at it

Comment thread src/actions/breakpoints.js Outdated
client,
sourceMaps,
pendingBreakpoint,
location
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not really clear to me if you need location because it's a composite of many things

Comment thread src/actions/breakpoints.js Outdated
}
return Object.assign({}, args, { status: "done" });
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move this try/catch to the firefox/commands side?

Comment thread src/actions/breakpoints.js Outdated

// If the breakpoint is already disabled, we need to communicate
// with the server, but we also have to remove the breakpoint immediately.
if (pendingBreakpoint.disabled) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to talk to the server if it's disabled, i think that means that the server doesn't have a bp?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point here. Originally, my thinking is that we need to verify our breakpoints with the server, and the only way to do that (at the time at least) was to add a breakpoint to the server. If its disabled we need to then remove it. Otherwise we have the weird behavior of disabled breakpoints in places that don't make sense.

Better solution: change the client code so we can get a breakpoint location. I haven't had a chance yet to try to make this better but it feels like it should be possible

@codehag codehag force-pushed the bug/2904-breakpoints-sourcemaps-breakpoints-are-out-of-sync-with-the-server-on-reload branch 2 times, most recently from c84a0c8 to 6729db6 Compare June 2, 2017 16:41
@codehag codehag changed the title [WIP] [Breakpoints] + [SourceMaps] breakpoints are out of sync with the server on reload [Breakpoints] + [SourceMaps] breakpoints are out of sync with the server on reload Jun 2, 2017
Copy link
Copy Markdown
Contributor

@jasonLaster jasonLaster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here are some random thoughts

Comment thread src/utils/prefs.js Outdated
prefs.debuggerVersion = DEBUGGER_VERSION;
}

module.exports = { prefs, setDebuggerVersion };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be nice to keep the schema version private so that we only manage it here...

if (prefs.debuggerPrefsSchemaVersion != prefsSchemaVersion) {
  prefs.clear()
  prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great idea

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the best place to implement the clear function would be in devtool-module -- i added a todo note, shall i make a separate issue?

Comment thread src/reducers/breakpoints.js Outdated
}

// Syncing Methods
function _optimisticlyAddBreakpoint(state, breakpoint) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: not sure if we need the _. I think everything is assumed to be private

},
"location": Object {
"column": undefined,
"line": 7,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's weird that generatedLocation and location are different line numbers

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, that might have been a bug later on

const endBps = selectors.getBreakpoints(getState());
const returnedBp = endBps.get(expectedId);
expect(returnedBp).not.toBe(bp);
expect(returnedBp.location).toEqual(correctedLocation);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be nice to have a snapshot of returnedBp here as well

Comment thread src/client/firefox/commands.js Outdated
delete bpClients[breakpointId];
return bpClient.remove();
} catch (_error) {
console.info("No breakpoint to delete on server");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe console.warn...

Comment thread src/reducers/breakpoints.js Outdated
const location = overrides.location || breakpoint.location;
const id = makeLocationId(location);
const updatedOpts = { ...overrides, loading: false };
const updatedBreakpoint = updateObj(breakpoint, updatedOpts);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think updateObj has outlived it's purpose.
I'd also try to be more aggressive about what we want to do.

function updateBreakpoint(state, newBreakpoint, oldBreakpoint) {
  const id = makeLocationId(newBreakpoint.location)
  state.setIn(["breakpoints", id], {
    ...breakpoint,
    ...overrides,
    loading: false
  });
}

@codehag codehag force-pushed the bug/2904-breakpoints-sourcemaps-breakpoints-are-out-of-sync-with-the-server-on-reload branch from 6729db6 to bc23496 Compare June 5, 2017 09:55
Comment thread src/actions/breakpoints.js Outdated
breakpoint,
[PROMISE]: promise
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: now that we've simplified this I think we can inline everything

dispatch({ type: SYNC_BREAKPOINT, [PROMISE]: syncClientBreakpoint() })

Comment thread src/actions/breakpoints.js Outdated
)
})
);
return dispatch(action);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can also inline this action too i think

Comment thread src/utils/prefs.js Outdated
// TODO: replace this with an implementation from devtools-modules
prefs.clear = () => {
prefs.pendingBreakpoints = getDefaultPref("pendingBreakpoints");
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function clearPrefs() {
  prefs.pendingBreakpoints = getDefaultPref("pendingBreakpoints");
}

Comment thread src/utils/prefs.js Outdated
};

return defaults[prefName];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can get the default for a pref.

jasonLaster and others added 7 commits June 5, 2017 09:22
@jasonLaster jasonLaster merged commit 0329e1a into firefox-devtools:master Jun 5, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants