Skip to content

Commit 6bb3cd3

Browse files
author
Aleksandar Gradinac
committed
[GR-28898] Automatic conditional configuration generation.
PullRequest: graal/10936
2 parents 7795578 + e40f3f8 commit 6bb3cd3

File tree

57 files changed

+2592
-966
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2592
-966
lines changed

substratevm/mx.substratevm/mx_substratevm.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ def is_musl_supported():
121121
return False
122122

123123

124+
def build_native_image_agent(native_image):
125+
agentfile = mx_subst.path_substitutions.substitute('<lib:native-image-agent>')
126+
agentname = agentfile.rsplit('.', 1)[0] # remove platform-specific file extension
127+
native_image(['--macro:native-image-agent-library', '-H:Name=' + agentname, '-H:Path=' + svmbuild_dir()])
128+
return svmbuild_dir() + '/' + agentfile
129+
130+
124131
class GraalVMConfig(collections.namedtuple('GraalVMConfig', 'primary_suite_dir, dynamicimports, disable_libpolyglot, force_bash_launchers, skip_libraries, exclude_components, native_images')):
125132
@classmethod
126133
def build(cls, primary_suite_dir=None, dynamicimports=None, disable_libpolyglot=True, force_bash_launchers=True, skip_libraries=True,
@@ -218,7 +225,8 @@ def __getattr__(self, name):
218225
'benchmarktest',
219226
"nativeimagehelp",
220227
'muslcbuild',
221-
'hellomodule'
228+
'hellomodule',
229+
'condconfig',
222230
])
223231

224232
def vm_native_image_path(config=None):
@@ -380,6 +388,11 @@ def svm_gate_body(args, tasks):
380388
with native_image_context(IMAGE_ASSERTION_FLAGS) as native_image:
381389
native_unittests_task()
382390

391+
with Task('conditional configuration tests', tasks, tags=[GraalTags.condconfig]) as t:
392+
if t:
393+
with native_image_context(IMAGE_ASSERTION_FLAGS) as native_image:
394+
conditional_config_task(native_image)
395+
383396
with Task('native unittests quickbuild', tasks, tags=[GraalTags.test_quickbuild]) as t:
384397
if t:
385398
with native_image_context(IMAGE_ASSERTION_FLAGS) as native_image:
@@ -480,6 +493,32 @@ def native_unittests_task(extra_build_args=None):
480493
native_unittest(['--builder-on-modulepath', '--build-args', _native_unittest_features] + additional_build_args)
481494

482495

496+
def conditional_config_task(native_image):
497+
agent_path = build_native_image_agent(native_image)
498+
config_dir = join(svmbuild_dir(), 'cond-config-test-config')
499+
if exists(config_dir):
500+
mx.rmtree(config_dir)
501+
conditional_config_filter_path = join(svmbuild_dir(), 'conditional-config-filter.json')
502+
with open(conditional_config_filter_path, 'w') as conditional_config_filter:
503+
conditional_config_filter.write(
504+
'''
505+
{
506+
"rules": [
507+
{"includeClasses": "com.oracle.svm.configure.test.conditionalconfig.**"}
508+
]
509+
}
510+
'''
511+
)
512+
agent_opts = ['config-output-dir=' + config_dir, 'experimental-conditional-config-filter-file=' + conditional_config_filter_path]
513+
jvm_unittest(['-agentpath:' + agent_path + '=' + ','.join(agent_opts),
514+
'-Dcom.oracle.svm.configure.test.conditionalconfig.ConfigurationGenerator.enabled=true',
515+
'com.oracle.svm.configure.test.conditionalconfig.ConfigurationGenerator'])
516+
517+
jvm_unittest(['-Dcom.oracle.svm.configure.test.conditionalconfig.ConfigurationVerifier.configpath=' + config_dir,
518+
"-Dcom.oracle.svm.configure.test.conditionalconfig.ConfigurationVerifier.enabled=true",
519+
'com.oracle.svm.configure.test.conditionalconfig.ConfigurationVerifier'])
520+
521+
483522
def javac_image_command(javac_path):
484523
return [join(javac_path, 'javac'), '-proc:none'] + (
485524
# We need to set java.home as com.sun.tools.javac.file.Locations.<clinit> can't handle `null`.
@@ -572,6 +611,10 @@ def _native_unittest(native_image, cmdline_args):
572611
_native_junit(native_image, unittest_args, unmask(pargs.build_args), unmask(pargs.run_args), blacklist, whitelist, pargs.preserve_image, builder_on_modulepath)
573612

574613

614+
def jvm_unittest(args):
615+
return mx_unittest.unittest(['--suite', 'substratevm'] + args)
616+
617+
575618
def js_image_test(jslib, bench_location, name, warmup_iterations, iterations, timeout=None, bin_args=None):
576619
bin_args = bin_args if bin_args is not None else []
577620
jsruncmd = [get_js_launcher(jslib)] + bin_args + [join(bench_location, 'harness.js'), '--', join(bench_location, name + '.js'),

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgent.java

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,24 @@
5454
import org.graalvm.nativeimage.ProcessProperties;
5555
import org.graalvm.nativeimage.hosted.Feature;
5656

57+
import com.oracle.svm.agent.conditionalconfig.ConditionalConfigurationWriter;
58+
import com.oracle.svm.agent.configwithorigins.ConfigurationWithOriginsWriter;
59+
import com.oracle.svm.agent.configwithorigins.MethodInfoRecordKeeper;
5760
import com.oracle.svm.agent.ignoredconfig.AgentMetaInfProcessor;
58-
import com.oracle.svm.agent.predicatedconfig.ConfigurationWithOriginsResultWriter;
59-
import com.oracle.svm.agent.predicatedconfig.MethodInfoRecordKeeper;
6061
import com.oracle.svm.agent.stackaccess.EagerlyLoadedJavaStackAccess;
6162
import com.oracle.svm.agent.stackaccess.InterceptedState;
6263
import com.oracle.svm.agent.stackaccess.OnDemandJavaStackAccess;
6364
import com.oracle.svm.agent.tracing.ConfigurationResultWriter;
6465
import com.oracle.svm.agent.tracing.TraceFileWriter;
6566
import com.oracle.svm.agent.tracing.core.Tracer;
6667
import com.oracle.svm.agent.tracing.core.TracingResultWriter;
68+
import com.oracle.svm.configure.config.ConditionalConfigurationPredicate;
69+
import com.oracle.svm.configure.config.ConfigurationFileCollection;
6770
import com.oracle.svm.configure.config.ConfigurationSet;
71+
import com.oracle.svm.configure.filters.ComplexFilter;
72+
import com.oracle.svm.configure.filters.ConfigurationFilter;
6873
import com.oracle.svm.configure.filters.FilterConfigurationParser;
69-
import com.oracle.svm.configure.filters.RuleNode;
74+
import com.oracle.svm.configure.filters.HierarchyFilterNode;
7075
import com.oracle.svm.configure.trace.AccessAdvisor;
7176
import com.oracle.svm.configure.trace.TraceProcessor;
7277
import com.oracle.svm.core.SubstrateUtil;
@@ -114,8 +119,8 @@ protected JNIHandleSet constructJavaHandles(JNIEnvironment env) {
114119
protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, String options) {
115120
String traceOutputFile = null;
116121
String configOutputDir = null;
117-
ConfigurationSet mergeConfigs = new ConfigurationSet();
118-
ConfigurationSet omittedConfigs = new ConfigurationSet();
122+
ConfigurationFileCollection mergeConfigs = new ConfigurationFileCollection();
123+
ConfigurationFileCollection omittedConfigs = new ConfigurationFileCollection();
119124
boolean builtinCallerFilter = true;
120125
boolean builtinHeuristicFilter = true;
121126
List<String> callerFilterFiles = new ArrayList<>();
@@ -125,6 +130,8 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
125130
boolean experimentalOmitClasspathConfig = false;
126131
boolean build = false;
127132
boolean configurationWithOrigins = false;
133+
List<String> conditionalConfigUserPackageFilterFiles = new ArrayList<>();
134+
List<String> conditionalConfigClassNameFilterFiles = new ArrayList<>();
128135
int configWritePeriod = -1; // in seconds
129136
int configWritePeriodInitialDelay = 1; // in seconds
130137
boolean trackReflectionMetadata = true;
@@ -196,6 +203,10 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
196203
build = Boolean.parseBoolean(getTokenValue(token));
197204
} else if (token.equals("experimental-configuration-with-origins")) {
198205
configurationWithOrigins = true;
206+
} else if (token.startsWith("experimental-conditional-config-filter-file=")) {
207+
conditionalConfigUserPackageFilterFiles.add(getTokenValue(token));
208+
} else if (token.startsWith("conditional-config-class-filter-file=")) {
209+
conditionalConfigClassNameFilterFiles.add(getTokenValue(token));
199210
} else if (token.equals("track-reflection-metadata")) {
200211
trackReflectionMetadata = true;
201212
} else if (token.startsWith("track-reflection-metadata=")) {
@@ -210,6 +221,10 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
210221
inform("no output/build options provided, tracking dynamic accesses and writing configuration to directory: " + configOutputDir);
211222
}
212223

224+
if (configurationWithOrigins && !conditionalConfigUserPackageFilterFiles.isEmpty()) {
225+
return error(5, "The agent can only be used in either the configuration with origins mode or the predefined classes mode.");
226+
}
227+
213228
if (configurationWithOrigins && !mergeConfigs.isEmpty()) {
214229
configurationWithOrigins = false;
215230
inform("using configuration with origins with configuration merging is currently unsupported. Disabling configuration with origins mode.");
@@ -219,30 +234,34 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
219234
warn("using experimental configuration with origins mode. Note that native-image cannot process these files, and this flag may change or be removed without a warning!");
220235
}
221236

222-
RuleNode callerFilter = null;
237+
ComplexFilter callerFilter = null;
238+
HierarchyFilterNode callerFilterHierarchyFilterNode = null;
223239
if (!builtinCallerFilter) {
224-
callerFilter = RuleNode.createRoot();
225-
callerFilter.addOrGetChildren("**", RuleNode.Inclusion.Include);
240+
callerFilterHierarchyFilterNode = HierarchyFilterNode.createInclusiveRoot();
241+
callerFilter = new ComplexFilter(callerFilterHierarchyFilterNode);
226242
}
243+
227244
if (!callerFilterFiles.isEmpty()) {
228-
if (callerFilter == null) {
229-
callerFilter = AccessAdvisor.copyBuiltinCallerFilterTree();
245+
if (callerFilterHierarchyFilterNode == null) {
246+
callerFilterHierarchyFilterNode = AccessAdvisor.copyBuiltinCallerFilterTree();
247+
callerFilter = new ComplexFilter(callerFilterHierarchyFilterNode);
230248
}
231249
if (!parseFilterFiles(callerFilter, callerFilterFiles)) {
232250
return 1;
233251
}
234252
}
235253

236-
RuleNode accessFilter = null;
254+
ComplexFilter accessFilter = null;
237255
if (!accessFilterFiles.isEmpty()) {
238-
accessFilter = AccessAdvisor.copyBuiltinAccessFilterTree();
256+
accessFilter = new ComplexFilter(AccessAdvisor.copyBuiltinAccessFilterTree());
239257
if (!parseFilterFiles(accessFilter, accessFilterFiles)) {
240258
return 1;
241259
}
242260
}
243261

244-
final MethodInfoRecordKeeper recordKeeper = new MethodInfoRecordKeeper(configurationWithOrigins);
245-
final Supplier<InterceptedState> interceptedStateSupplier = configurationWithOrigins ? EagerlyLoadedJavaStackAccess.stackAccessSupplier()
262+
boolean shouldTraceOriginInformation = configurationWithOrigins || !conditionalConfigUserPackageFilterFiles.isEmpty();
263+
final MethodInfoRecordKeeper recordKeeper = new MethodInfoRecordKeeper(shouldTraceOriginInformation);
264+
final Supplier<InterceptedState> interceptedStateSupplier = shouldTraceOriginInformation ? EagerlyLoadedJavaStackAccess.stackAccessSupplier()
246265
: OnDemandJavaStackAccess.stackAccessSupplier();
247266

248267
if (configOutputDir != null) {
@@ -273,21 +292,39 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
273292
ignoreConfigFromClasspath(jvmti, omittedConfigs);
274293
}
275294
AccessAdvisor advisor = createAccessAdvisor(builtinHeuristicFilter, callerFilter, accessFilter);
276-
TraceProcessor omittedConfigProcessor = null;
295+
TraceProcessor processor = new TraceProcessor(advisor);
296+
ConfigurationSet omittedConfiguration = new ConfigurationSet();
277297
Predicate<String> shouldExcludeClassesWithHash = null;
278298
if (!omittedConfigs.isEmpty()) {
279299
Function<IOException, Exception> ignore = e -> {
280300
warn("Failed to load omitted config: " + e);
281301
return null;
282302
};
283-
omittedConfigProcessor = new TraceProcessor(advisor, omittedConfigs.loadJniConfig(ignore), omittedConfigs.loadReflectConfig(ignore),
284-
omittedConfigs.loadProxyConfig(ignore), omittedConfigs.loadResourceConfig(ignore), omittedConfigs.loadSerializationConfig(ignore),
285-
omittedConfigs.loadPredefinedClassesConfig(null, null, ignore), null);
286-
shouldExcludeClassesWithHash = omittedConfigProcessor.getPredefinedClassesConfiguration()::containsClassWithHash;
303+
omittedConfiguration = omittedConfigs.loadConfigurationSet(ignore, null, null);
304+
shouldExcludeClassesWithHash = omittedConfiguration.getPredefinedClassesConfiguration()::containsClassWithHash;
287305
}
288306

289307
if (configurationWithOrigins) {
290-
ConfigurationWithOriginsResultWriter writer = new ConfigurationWithOriginsResultWriter(advisor, recordKeeper);
308+
ConfigurationWithOriginsWriter writer = new ConfigurationWithOriginsWriter(processor, recordKeeper);
309+
tracer = writer;
310+
tracingResultWriter = writer;
311+
} else if (!conditionalConfigUserPackageFilterFiles.isEmpty()) {
312+
ComplexFilter userCodeFilter = new ComplexFilter(HierarchyFilterNode.createRoot());
313+
if (!parseFilterFiles(userCodeFilter, conditionalConfigUserPackageFilterFiles)) {
314+
return 2;
315+
}
316+
ComplexFilter classNameFilter;
317+
if (!conditionalConfigClassNameFilterFiles.isEmpty()) {
318+
classNameFilter = new ComplexFilter(HierarchyFilterNode.createRoot());
319+
if (!parseFilterFiles(classNameFilter, conditionalConfigClassNameFilterFiles)) {
320+
return 3;
321+
}
322+
} else {
323+
classNameFilter = new ComplexFilter(HierarchyFilterNode.createInclusiveRoot());
324+
}
325+
326+
ConditionalConfigurationPredicate predicate = new ConditionalConfigurationPredicate(classNameFilter);
327+
ConditionalConfigurationWriter writer = new ConditionalConfigurationWriter(processor, recordKeeper, userCodeFilter, predicate);
291328
tracer = writer;
292329
tracingResultWriter = writer;
293330
} else {
@@ -302,10 +339,9 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
302339
}
303340
return e; // rethrow
304341
};
305-
TraceProcessor processor = new TraceProcessor(advisor, mergeConfigs.loadJniConfig(handler), mergeConfigs.loadReflectConfig(handler),
306-
mergeConfigs.loadProxyConfig(handler), mergeConfigs.loadResourceConfig(handler), mergeConfigs.loadSerializationConfig(handler),
307-
mergeConfigs.loadPredefinedClassesConfig(predefinedClassDestDirs, shouldExcludeClassesWithHash, handler), omittedConfigProcessor);
308-
ConfigurationResultWriter writer = new ConfigurationResultWriter(processor);
342+
343+
ConfigurationSet configuration = mergeConfigs.loadConfigurationSet(handler, predefinedClassDestDirs, shouldExcludeClassesWithHash);
344+
ConfigurationResultWriter writer = new ConfigurationResultWriter(processor, configuration, omittedConfiguration);
309345
tracer = writer;
310346
tracingResultWriter = writer;
311347
}
@@ -368,7 +404,7 @@ private static <T> T usage(T result, String message) {
368404
return result;
369405
}
370406

371-
private static AccessAdvisor createAccessAdvisor(boolean builtinHeuristicFilter, RuleNode callerFilter, RuleNode accessFilter) {
407+
private static AccessAdvisor createAccessAdvisor(boolean builtinHeuristicFilter, ConfigurationFilter callerFilter, ConfigurationFilter accessFilter) {
372408
AccessAdvisor advisor = new AccessAdvisor();
373409
advisor.setHeuristicsEnabled(builtinHeuristicFilter);
374410
if (callerFilter != null) {
@@ -388,15 +424,15 @@ private static int parseIntegerOrNegative(String number) {
388424
}
389425
}
390426

391-
private static boolean parseFilterFiles(RuleNode filter, List<String> filterFiles) {
427+
private static boolean parseFilterFiles(ComplexFilter filter, List<String> filterFiles) {
392428
for (String path : filterFiles) {
393429
try {
394430
new FilterConfigurationParser(filter).parseAndRegister(Paths.get(path).toUri());
395431
} catch (Exception e) {
396432
return error(false, "cannot parse filter file " + path + ": " + e);
397433
}
398434
}
399-
filter.removeRedundantNodes();
435+
filter.getHierarchyFilterNode().removeRedundantNodes();
400436
return true;
401437
}
402438

@@ -422,7 +458,7 @@ private void setupExecutorServiceForPeriodicConfigurationCapture(int writePeriod
422458
initialDelay, writePeriod, TimeUnit.SECONDS);
423459
}
424460

425-
private static void ignoreConfigFromClasspath(JvmtiEnv jvmti, ConfigurationSet ignoredConfigSet) {
461+
private static void ignoreConfigFromClasspath(JvmtiEnv jvmti, ConfigurationFileCollection ignoredConfigCollection) {
426462
String classpath = Support.getSystemProperty(jvmti, "java.class.path");
427463
String sep = Support.getSystemProperty(jvmti, "path.separator");
428464
if (sep == null) {
@@ -436,7 +472,7 @@ private static void ignoreConfigFromClasspath(JvmtiEnv jvmti, ConfigurationSet i
436472
}
437473
}
438474

439-
AgentMetaInfProcessor processor = new AgentMetaInfProcessor(ignoredConfigSet);
475+
AgentMetaInfProcessor processor = new AgentMetaInfProcessor(ignoredConfigCollection);
440476
for (String cpEntry : classpath.split(sep)) {
441477
try {
442478
NativeImageMetaInfWalker.walkMetaInfForCPEntry(Paths.get(cpEntry), processor);

0 commit comments

Comments
 (0)