Search code examples
javaremote-debugginggraalvmgraaljs

Can you remotely debug Graal.js script running inside Graal JVM?


I am running Java program on Graal JVM (graalvm-ce-java8-19.3.0), and inside that program I run JS scripts via Graal.js engine. I want to configure remote debugging connection for that JS script (not the Java program). I already found that Graal has support for that kind of debugging via Chrome DevTools protocol/standard.

Documentation explains how to use standalone Graal.js interpreter, but it also says that you can use same options in JVM by prefixing them with -Dpolyglot.

So instead of --inspect you need to use -Dpolyglot.inspect:

java -Dpolyglot.inspect=9229 -jar app.jar

And this works perfect for local debugging - all as described here and here.

Now I tried to use the same for remote debugging, by setting -Dpolyglot.inspect=exampleHost:9229, where exampleHost resolves to my external ip. Unfortunately this setting prevents Graal.js engine to initialize correctly. There is not much indication on what is really wrong, just this msg on stderr:

ScriptEngineManager providers.next(): javax.script.ScriptEngineFactory: 
Provider com.oracle.truffle.js.scriptengine.GraalJSEngineFactory could not be instantiated

Does it mean that remote configuration is not supported? What is wrong here?


Solution

  • It turns out that for remote connections Graal by default wants to use a secure debugging channel, so you need to either disable that by -Dpolyglot.inspect.Secure=false or configure properly using -Dpolyglot.inspect.KeyStore* switches.

    As for my case that security was not needed, so my config became:

        java -Dpolyglot.inspect=exampleHost:9229 -Dpolyglot.inspect.Secure=false -jar app.jar
    

    I just had to add exampleHost:9229 to chrome://inspect/#devices -> Configure...

    And remote debugging works!

    TL;DR

    Here is how I found the reason of configuration problem. It may be useful in other similar cases.

    I debugged the problematic Java program to see what kind of problem occurs in that ScriptEngineManager, I put a breakpoint in javax.script.ScriptEngineManager#initEngines in line containing message "ScriptEngineManager providers.next():". It turned out that this class has an internal DEBUG constant hard-coded to false, which prevents printing any stacktraces to stderr. I dumped the exception manually to stderr:

     oracle.truffle.js.scriptengine.GraalJSEngineFactory could not be instantiated
        at java.util.ServiceLoader.fail(ServiceLoader.java:232)
        at java.util.ServiceLoader.access$100(ServiceLoader.java:185)
        at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:384)
        at java.util.ServiceLoader$LazyIterator.access$700(ServiceLoader.java:323)
        at java.util.ServiceLoader$LazyIterator$2.run(ServiceLoader.java:407)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:409)
        at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
        at javax.script.ScriptEngineManager.initEngines(ScriptEngineManager.java:122)
        at javax.script.ScriptEngineManager.init(ScriptEngineManager.java:84)
        at javax.script.ScriptEngineManager.<init>(ScriptEngineManager.java:61)
        ...
     Caused by: org.graalvm.polyglot.PolyglotException: Starting inspector on exampleHost:9229 failed: Use options to specify the keystore
        at org.graalvm.polyglot.Engine$Builder.build(Engine.java:506)
        at com.oracle.truffle.js.scriptengine.GraalJSEngineFactory.<init>(GraalJSEngineFactory.java:95)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at java.lang.Class.newInstance(Class.java:442)
        at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:380)
        ... 17 more
    

    Now this was just a matter of finding out how to set or disable that required keystore.