oracle / graal

GraalVM compiles Java applications into native executables that start instantly, scale fast, and use fewer compute resources 🚀
https://www.graalvm.org
Other
19.99k stars 1.6k forks source link

Native image agent configuration filtering not working well for resources configuration #8417

Open galderz opened 4 months ago

galderz commented 4 months ago

The native image agent configuration filters don't work well with resource configuration.

For example, say you have this call I want to filter out (coming from resources-origins.txt generated using experimental-configuration-with-origins) option:

├── org.apache.maven.surefire.booter.ForkedBooter#main(java.lang.String[])
│   └── org.apache.maven.surefire.booter.ForkedBooter#run(org.apache.maven.surefire.booter.ForkedBooter,java.lang.String[])
│       ├── org.apache.maven.surefire.booter.ForkedBooter#setupBooter(java.lang.String,java.lang.String,java.lang.String,java.lang.String)
│       │   └── org.apache.maven.surefire.booter.ForkedBooter#lookupDecoderFactory(java.lang.String)
│       │       └── java.util.ServiceLoader$3#hasNext()
│       │           └── java.util.ServiceLoader$2#hasNext()
│       │               └── java.util.ServiceLoader$LazyClassPathLookupIterator#hasNext()
│       │                   └── java.util.ServiceLoader$LazyClassPathLookupIterator#hasNextService()
│       │                       └── java.util.ServiceLoader$LazyClassPathLookupIterator#nextProviderClass()
│       │                           └── java.lang.ClassLoader#getResources(java.lang.String) - {   "resources":{   "includes":[{     "pattern":"\\QMETA-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory\\E"   }]},   "bundles":[] }
│       │                               └── java.lang.ClassLoader#getResources(java.lang.String) - {   "resources":{   "includes":[{     "pattern":"\\QMETA-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory\\E"   }]},   "bundles":[] }

When you look at the trace-output JSON file it says:

{"tracer":"reflect", "function":"getResources", "class":"jdk.internal.loader.ClassLoaders$AppClassLoader", "caller_class":"java.util.ServiceLoader$LazyClassPathLookupIterator", "result":"true", "args":["META-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory"]},

The problem is that one cannot really filter java.util.ServiceLoader because that would filter out all service loader calls. What you want to filter is service loader calls coming from org.apache.maven.surefire package. Setting to exclude that package doesn't work because the caller does not match that package.

Here's the filter I used (set on both access-based and caller-based) filtes:

{
  "rules": [
    {
      "excludeClasses": "org.apache.maven.surefire.**"
    }]
}

The resulting resources configuration contains:

{
  "resources":{
    "includes":[{
      "pattern":"\\QMETA-INF/services/org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory\\E"
    }]
  }
}

This is not limited to just service loader, other JDK calls are also affected, e.g. ClassLoader.getResourceAsStream:

│                       │   │       └── io.quarkus.arc.processor.ClientProxyGenerator#generate(io.quarkus.arc.processor.BeanInfo,java.lang.String,java.util.function.Consumer,boolean)
│                       │   │           └── io.quarkus.arc.processor.Types#resolvedTypeVariables(org.jboss.jandex.ClassInfo,io.quarkus.arc.processor.BeanDeployment)
│                       │   │               └── io.quarkus.arc.processor.Types#getTypeClosure(org.jboss.jandex.ClassInfo,org.jboss.jandex.AnnotationTarget,java.util.Map,io.quarkus.arc.processor.BeanDeployment,java.util.function.BiConsumer,java.util.Set)
│                       │   │                   └── io.quarkus.arc.processor.Types#getTypeClosure(org.jboss.jandex.ClassInfo,org.jboss.jandex.AnnotationTarget,boolean,java.util.Map,io.quarkus.arc.processor.BeanDeployment,java.util.function.BiConsumer,java.util.Set,boolean)
│                       │   │                       └── io.quarkus.arc.processor.Types#getTypeClosure(org.jboss.jandex.ClassInfo,org.jboss.jandex.AnnotationTarget,boolean,java.util.Map,io.quarkus.arc.processor.BeanDeployment,java.util.function.BiConsumer,java.util.Set,boolean)
│                       │   │                           └── io.quarkus.arc.processor.Types#getTypeClosure(org.jboss.jandex.ClassInfo,org.jboss.jandex.AnnotationTarget,boolean,java.util.Map,io.quarkus.arc.processor.BeanDeployment,java.util.function.BiConsumer,java.util.Set,boolean)
│                       │   │                               └── io.quarkus.arc.processor.IndexClassLookupUtils#getClassByName(org.jboss.jandex.IndexView,org.jboss.jandex.DotName)
│                       │   │                                   └── io.quarkus.arc.processor.IndexClassLookupUtils#getClassByName(org.jboss.jandex.IndexView,org.jboss.jandex.DotName,boolean)
│                       │   │                                       └── io.quarkus.arc.processor.BeanArchives$IndexWrapper#getClassByName(org.jboss.jandex.DotName)
│                       │   │                                           └── java.util.concurrent.ConcurrentHashMap#computeIfAbsent(java.lang.Object,java.util.function.Function)
│                       │   │                                               └── io.quarkus.arc.processor.BeanArchives$IndexWrapper$$Lambda/0x0000000801479b78#apply(java.lang.Object)
│                       │   │                                                   └── io.quarkus.arc.processor.BeanArchives$IndexWrapper#computeAdditional(org.jboss.jandex.DotName)
│                       │   │                                                       └── io.quarkus.arc.processor.BeanArchives#index(org.jboss.jandex.Indexer,java.lang.String,java.lang.ClassLoader)
│                       │   │                                                           └── io.quarkus.bootstrap.classloading.QuarkusClassLoader#getResourceAsStream(java.lang.String)
│                       │   │                                                               └── io.quarkus.bootstrap.classloading.QuarkusClassLoader#getResourceAsStream(java.lang.String)
│                       │   │                                                                   └── java.lang.ClassLoader#getResourceAsStream(java.lang.String)
│                       │   │                                                                       └── java.lang.ClassLoader#getResource(java.lang.String) - {   "resources":{   "includes":[{     "pattern":"\\Qjava/lang/AutoCloseable.class\\E"   }, {     "pattern":"\\Qjava/util/concurrent/Executor.class\\E"   }]},   "bundles":[] }
│                       │   │                                                                           └── java.lang.ClassLoader#getResource(java.lang.String) - {   "resources":{   "includes":[{     "pattern":"\\Qjava/lang/AutoCloseable.class\\E"   }, {     "pattern":"\\Qjava/util/concurrent/Executor.class\\E"   }]},   "bundles":[] }
│                       │   │                                                                               └── jdk.internal.loader.BootLoader#findResource(java.lang.String)
│                       │   │                                                                                   └── jdk.internal.loader.BuiltinClassLoader#findResource(java.lang.String)
│                       │   │                                                                                       └── jdk.internal.loader.BuiltinClassLoader#findResource(java.lang.String,java.lang.String) - {   "resources":{   "includes":[{     "pattern":"java.base:\\Qjava/lang/AutoCloseable.class\\E"   }, {     "pattern":"java.base:\\Qjava/util/concurrent/Executor.class\\E"   }]},   "bundles":[] }

I have a filter in place for io.quarkus.** but the resources configuration file still contains a registration for AutoCloseable:

    "pattern":"java.base:\\Qjava/lang/AutoCloseable.class\\E"

If you look at the trace output it shows:

{"tracer":"reflect", "function":"getResource", "class":"jdk.internal.loader.ClassLoaders$AppClassLoader", "caller_class":"java.lang.ClassLoader", "result":"true", "args":["java/lang/AutoCloseable.class"]},
{"tracer":"reflect", "function":"getResource", "class":"jdk.internal.loader.ClassLoaders$PlatformClassLoader", "caller_class":"java.lang.ClassLoader", "result":"true", "args":["java/lang/AutoCloseable.class"]},
{"tracer":"reflect", "function":"findResource", "caller_class":"jdk.internal.loader.BuiltinClassLoader", "result":"true", "args":["java.base","java/lang/AutoCloseable.class"]},
kassifar commented 4 months ago

Hi @galderz , thank you for suggesting this feature for GraalVM.

kassifar commented 4 months ago

Hello @fniephaus, should I create a GR for this?

fniephaus commented 4 months ago

Yes, please.