provegard / ncdbg

A debugger for Nashorn that uses Chrome DevTools as frontend
BSD 3-Clause "New" or "Revised" License
31 stars 5 forks source link

Debugging after changing js #98

Closed ThePainnn closed 6 years ago

ThePainnn commented 6 years ago

Hi,

I'm making a vscode extension that can debug .js code from a java server. We start the server, start ncdbg and then hit F5 on vscode to start a debugging session. Breakpoint are being hit everything work as intended. I stop the debugging session, change the js file and then when I start the debugging again session:

Setting a breakpoint with ID ndb1 for location(s) nds2:2 (jdk.nashorn.internal.scripts.Script$Recompilation$18$47AA$test:2) in script with URL matching file:\/\/\/test\.js Won't create a stack frame for location (jdk.nashorn.internal.scripts.Script$Recompilation$18$47AA$test:2) since we don't recognize it.

Problem no breakpoint hit. Script run fine on the web server (got the right output). I need to restart the extension to hit a breakpoint again. I've try to restart ncdbg between run to make sure, but no luck. Web server file are updated and they run fine, it's only the breakpoint that won't work anymore.

Any idea?

Thanks a lot!

ThePainnn commented 6 years ago

Minor update: On every debug session I was restarting ncdbg. If I do not the error transform in:

NCDbg | stdout: Setting an unresolved breakpoint with ID ndb1 at line 2 in script with URL matching file:\/\/\/test\.js Again the server execution of the script give the good output before and after script modification.

Hope this helps.

provegard commented 6 years ago

When you change the JS file, is it hot reloaded? Or is it loaded on demand during some request to the server?

ThePainnn commented 6 years ago

I think I know what's going on. ncdbg creates version file of my test.js: test_ndx1.js, test_ndx2.js when I change the file. Those where not shown by the chrome debugger because I use sourceMap in tsconfig.json. If I disbale sourceMap, I see ndx files and can put breakpoint. I need to know the good version (ndx1, ndx2, etc...) and I'm all set to debug.

If I put sourceMap back to true, ndx files are hidden and breakpoint are skip because the debugger (most probably) can't map the ndx to original test.js.

Thing is, source maps are nice. Debug in ndx is ok as I'll tell my client what do to and they need to keep track of ndx version of there are keep changing their breakpoint accordingly.

Here the error when sourceMap is true (inlineSourceMap to true/false does no matter here):

NCDbg | stdout: Ignoring non-file source map URL: data:application/json;base64,eyJ2ZXJzaW9uvIjoqzLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiJkOi9zY3JpcHRpbmctZXh0LW1haW4vd29yay9zcmMvIiwic291cmNlcyI6WyJ0ZXN0LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE1BQU0sQ0FBQyxPQUFPLEdBQUcsd0JBQXdCLENBQVMsRUFBRSxDQUFTO0lBRXpELE9BQU8sSUFBSSxHQUFHLENBQUMsR0FBRyxLQUFLLEdBQUcsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0FBQzNDLENBQUMsQ0FBQSJ9 debug-server.js:83 NCDbg | stdout: Setting an unresolved breakpoint with ID ndb6 at line 2 in script with URL matching eval:\/\/\/Script

ThePainnn commented 6 years ago

I'm currently debugging your code. It's still does not work, but I've made 2 change that might help you understand what is going on.

  1. In SourceMap.scala:

private case class SourceMapData(sourceRoot: String, sources: Seq[String])

def sources: Seq[String] = data.sources .filter(_ != "") .map(x => data.sourceRoot + x)

I added the sourceRoot to have the complete path to the js file.

  1. In ScriptURL.scala:

    private def isAbsoluteUnixOrWindowsFilePath(x: String) = x.startsWith("/") || (x.lift(1).contains(':') && x.indexOf('\\') > 1) || (x.lift(1).contains(':') && x.indexOf('/') > 1)

This will enable window path like: "d:/scripting-main/work/src/" to be recognized as absolute path. The backslash is not always use on windows. We've got some slash when using vscode generated source maps.

It still does not break at the original file, but the map file are published under http://127.0.0.1:7778/files/d:/scripting-main/work/.out/test.js.map, which is a step forward.

provegard commented 6 years ago

Hm ok. The ndx versions exist because it's not really possible to remove an old version of a file - since the host code may in theory reference a function exposed by it.

But it may be possible to do some source map rewriting of course.

Is the data: source map URL relevant? I suppose it's easy to add support for it.

Regarding your code changes; feel free to open a PR. (I'm traveling, so I won't be able to do anything right now.)

ThePainnn commented 6 years ago

debug

This is what is server using the chrome debugger. The source map url go to test.js:

{"version":3,"file":"test.js","sourceRoot":"d:/scripting-ext-main/work/src/","sources":["test.js"],"names":[],"mappings":

but for a reason the test.js @ 127.0.0.:7778 has wrong path. It's like a concatenation of the .out path and the src path. The one in file:// has the good version.

provegard commented 6 years ago

I'm working on this. Could you clarify how you transpile files? The source file is "test.js" but so is the transpiled file. Is it something like ES7 -> ES5?

I guess that if NCDbg were to rewrite "file" in the source map to the latest ndx version, then things would work. I'll try something like that in a separate branch that you can try.

provegard commented 6 years ago

I have a new idea, which is to emit the changed script under the same url and give old scripts new urls (ndx). Will try and see how Chrome reacts.

ThePainnn commented 6 years ago

Hi, I manage to get this working by hacking the .map file. I've put the sourceRoot to "" instead of the path to the sourceMap. So that chrome find it.

image

As you can see my test.js file is very simple:

module.exports = function MyMainFunction(a: string, b: number) { return "s:" + a + " n " + b.toString(); }

with a source map of:

{"version":3,"file":"test.js","sourceRoot":"","sources":["test.js"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,GAAG,wBAAwB,CAAS,EAAE,CAAS;IAEzD,OAAO,IAAI,GAAG,CAAC,GAAG,UAAU,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;AAChD,CAAC,CAAA"}

It's directly es5, just transferred to the server as is and executed (we use something similar to https://github.com/coveo/nashorn-commonjs-modules to support commonJS module for nashorn).

This work, I can modify the file and it hit the breakpoint again. But, if I modify a script, execute and then revert my change. Breakpoints won't work anymore, most probably because of a source map issues with an older ndx version.

provegard commented 6 years ago

After some consideration I have come to the conclusion that I will remove support for old versions of a script (based on URL), so NCDbg will always only handle the last version. In other words, no ndx variants. I need to make some changes for this to work and test how DevTools behaves. I'll keep you posted.

ThePainnn commented 6 years ago

Nice, this should work. Thanks!

provegard commented 6 years ago

Could you please try the issue-98 branch?

In my testing, DevTools correctly picks up the replacement script and stops on "old" breakpoints in the replacement script. The only side-effect I have seen (though I don't think it matters) is that each breakpoint line gets two breakpoint locations:

image

ThePainnn commented 6 years ago

Cool, I'm on vacation for 2 weeks. I'll try it out asap when I come back. Thanks a bunch!

provegard commented 6 years ago

I went ahead and merged to master. Please reopen this issue if things don't work when you test!

ThePainnn commented 6 years ago

Hi just tested it. Works like a charm now :) Thanks a lot!

provegard commented 6 years ago

Good to hear! Thanks for letting me know!