use of com.oracle.svm.configure.filters.RuleNode in project graal by oracle.
the class NativeImageAgent method onLoadCallback.
@Override
protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, String options) {
String traceOutputFile = null;
String configOutputDir = null;
ConfigurationSet mergeConfigs = new ConfigurationSet();
ConfigurationSet omittedConfigs = new ConfigurationSet();
boolean builtinCallerFilter = true;
boolean builtinHeuristicFilter = true;
List<String> callerFilterFiles = new ArrayList<>();
List<String> accessFilterFiles = new ArrayList<>();
boolean experimentalClassLoaderSupport = true;
boolean experimentalClassDefineSupport = false;
boolean experimentalOmitClasspathConfig = false;
boolean build = false;
boolean configurationWithOrigins = false;
// in seconds
int configWritePeriod = -1;
// in seconds
int configWritePeriodInitialDelay = 1;
boolean trackReflectionMetadata = true;
String[] tokens = !options.isEmpty() ? options.split(",") : new String[0];
for (String token : tokens) {
if (token.startsWith("trace-output=")) {
if (traceOutputFile != null) {
return usage(1, "cannot specify trace-output= more than once.");
}
traceOutputFile = getTokenValue(token);
} else if (token.startsWith("config-output-dir=") || token.startsWith("config-merge-dir=")) {
if (configOutputDir != null) {
return usage(1, "cannot specify more than one of config-output-dir= or config-merge-dir=.");
}
configOutputDir = transformPath(getTokenValue(token));
if (token.startsWith("config-merge-dir=")) {
mergeConfigs.addDirectory(Paths.get(configOutputDir));
}
} else if (token.startsWith("config-to-omit=")) {
String omittedConfigDir = getTokenValue(token);
omittedConfigDir = transformPath(omittedConfigDir);
omittedConfigs.addDirectory(Paths.get(omittedConfigDir));
} else if (token.equals("experimental-omit-config-from-classpath")) {
experimentalOmitClasspathConfig = true;
} else if (token.startsWith("experimental-omit-config-from-classpath=")) {
experimentalOmitClasspathConfig = Boolean.parseBoolean(getTokenValue(token));
} else if (token.startsWith("restrict-all-dir") || token.equals("restrict") || token.startsWith("restrict=")) {
warn("restrict mode is no longer supported, ignoring option: " + token);
} else if (token.equals("no-builtin-caller-filter")) {
builtinCallerFilter = false;
} else if (token.startsWith("builtin-caller-filter=")) {
builtinCallerFilter = Boolean.parseBoolean(getTokenValue(token));
} else if (token.equals("no-builtin-heuristic-filter")) {
builtinHeuristicFilter = false;
} else if (token.startsWith("builtin-heuristic-filter=")) {
builtinHeuristicFilter = Boolean.parseBoolean(getTokenValue(token));
} else if (token.equals("no-filter")) {
// legacy
builtinCallerFilter = false;
builtinHeuristicFilter = false;
} else if (token.startsWith("no-filter=")) {
// legacy
builtinCallerFilter = !Boolean.parseBoolean(getTokenValue(token));
builtinHeuristicFilter = builtinCallerFilter;
} else if (token.startsWith("caller-filter-file=")) {
callerFilterFiles.add(getTokenValue(token));
} else if (token.startsWith("access-filter-file=")) {
accessFilterFiles.add(getTokenValue(token));
} else if (token.equals("experimental-class-loader-support")) {
experimentalClassLoaderSupport = true;
} else if (token.startsWith("experimental-class-loader-support=")) {
experimentalClassLoaderSupport = Boolean.parseBoolean(getTokenValue(token));
} else if (token.equals("experimental-class-define-support")) {
experimentalClassDefineSupport = true;
} else if (token.startsWith("experimental-class-define-support=")) {
experimentalClassDefineSupport = Boolean.parseBoolean(getTokenValue(token));
} else if (token.startsWith("config-write-period-secs=")) {
configWritePeriod = parseIntegerOrNegative(getTokenValue(token));
if (configWritePeriod <= 0) {
return usage(1, "config-write-period-secs must be an integer greater than 0");
}
} else if (token.startsWith("config-write-initial-delay-secs=")) {
configWritePeriodInitialDelay = parseIntegerOrNegative(getTokenValue(token));
if (configWritePeriodInitialDelay < 0) {
return usage(1, "config-write-initial-delay-secs must be an integer greater or equal to 0");
}
} else if (token.equals("build")) {
build = true;
} else if (token.startsWith("build=")) {
build = Boolean.parseBoolean(getTokenValue(token));
} else if (token.equals("experimental-configuration-with-origins")) {
configurationWithOrigins = true;
} else if (token.equals("track-reflection-metadata")) {
trackReflectionMetadata = true;
} else if (token.startsWith("track-reflection-metadata=")) {
trackReflectionMetadata = Boolean.parseBoolean(getTokenValue(token));
} else {
return usage(1, "unknown option: '" + token + "'.");
}
}
if (traceOutputFile == null && configOutputDir == null && !build) {
configOutputDir = transformPath(AGENT_NAME + "_config-pid{pid}-{datetime}/");
inform("no output/build options provided, tracking dynamic accesses and writing configuration to directory: " + configOutputDir);
}
if (configurationWithOrigins && !mergeConfigs.isEmpty()) {
configurationWithOrigins = false;
inform("using configuration with origins with configuration merging is currently unsupported. Disabling configuration with origins mode.");
}
if (configurationWithOrigins) {
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!");
}
RuleNode callerFilter = null;
if (!builtinCallerFilter) {
callerFilter = RuleNode.createRoot();
callerFilter.addOrGetChildren("**", RuleNode.Inclusion.Include);
}
if (!callerFilterFiles.isEmpty()) {
if (callerFilter == null) {
callerFilter = AccessAdvisor.copyBuiltinCallerFilterTree();
}
if (!parseFilterFiles(callerFilter, callerFilterFiles)) {
return 1;
}
}
RuleNode accessFilter = null;
if (!accessFilterFiles.isEmpty()) {
accessFilter = AccessAdvisor.copyBuiltinAccessFilterTree();
if (!parseFilterFiles(accessFilter, accessFilterFiles)) {
return 1;
}
}
final MethodInfoRecordKeeper recordKeeper = new MethodInfoRecordKeeper(configurationWithOrigins);
final Supplier<InterceptedState> interceptedStateSupplier = configurationWithOrigins ? EagerlyLoadedJavaStackAccess.stackAccessSupplier() : OnDemandJavaStackAccess.stackAccessSupplier();
if (configOutputDir != null) {
if (traceOutputFile != null) {
return usage(1, "can only once specify exactly one of trace-output=, config-output-dir= or config-merge-dir=.");
}
try {
configOutputDirPath = Files.createDirectories(Path.of(configOutputDir));
configOutputLockFilePath = configOutputDirPath.resolve(ConfigurationFile.LOCK_FILE_NAME);
try {
Files.writeString(configOutputLockFilePath, Long.toString(ProcessProperties.getProcessID()), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
} catch (FileAlreadyExistsException e) {
String process;
try {
process = Files.readString(configOutputLockFilePath).stripTrailing();
} catch (Exception ignored) {
process = "(unknown)";
}
return error(2, "Output directory '" + configOutputDirPath + "' is locked by process " + process + ", " + "which means another agent instance is already writing to this directory. " + "Only one agent instance can safely write to a specific target directory at the same time. " + "Unless file '" + ConfigurationFile.LOCK_FILE_NAME + "' is a leftover from an earlier process that terminated abruptly, it is unsafe to delete it. " + "For running multiple processes with agents at the same time to create a single configuration, read Agent.md " + "or https://www.graalvm.org/reference-manual/native-image/Agent/ on how to use the native-image-configure tool.");
}
if (experimentalOmitClasspathConfig) {
ignoreConfigFromClasspath(jvmti, omittedConfigs);
}
AccessAdvisor advisor = createAccessAdvisor(builtinHeuristicFilter, callerFilter, accessFilter);
TraceProcessor omittedConfigProcessor = null;
Predicate<String> shouldExcludeClassesWithHash = null;
if (!omittedConfigs.isEmpty()) {
Function<IOException, Exception> ignore = e -> {
warn("Failed to load omitted config: " + e);
return null;
};
omittedConfigProcessor = new TraceProcessor(advisor, omittedConfigs.loadJniConfig(ignore), omittedConfigs.loadReflectConfig(ignore), omittedConfigs.loadProxyConfig(ignore), omittedConfigs.loadResourceConfig(ignore), omittedConfigs.loadSerializationConfig(ignore), omittedConfigs.loadPredefinedClassesConfig(null, null, ignore), null);
shouldExcludeClassesWithHash = omittedConfigProcessor.getPredefinedClassesConfiguration()::containsClassWithHash;
}
if (configurationWithOrigins) {
ConfigurationWithOriginsResultWriter writer = new ConfigurationWithOriginsResultWriter(advisor, recordKeeper);
tracer = writer;
tracingResultWriter = writer;
} else {
Path[] predefinedClassDestDirs = { Files.createDirectories(configOutputDirPath.resolve(ConfigurationFile.PREDEFINED_CLASSES_AGENT_EXTRACTED_SUBDIR)) };
Function<IOException, Exception> handler = e -> {
if (e instanceof NoSuchFileException) {
warn("file " + ((NoSuchFileException) e).getFile() + " for merging could not be found, skipping");
return null;
} else if (e instanceof FileNotFoundException) {
warn("could not open configuration file: " + e);
return null;
}
// rethrow
return e;
};
TraceProcessor processor = new TraceProcessor(advisor, mergeConfigs.loadJniConfig(handler), mergeConfigs.loadReflectConfig(handler), mergeConfigs.loadProxyConfig(handler), mergeConfigs.loadResourceConfig(handler), mergeConfigs.loadSerializationConfig(handler), mergeConfigs.loadPredefinedClassesConfig(predefinedClassDestDirs, shouldExcludeClassesWithHash, handler), omittedConfigProcessor);
ConfigurationResultWriter writer = new ConfigurationResultWriter(processor);
tracer = writer;
tracingResultWriter = writer;
}
expectedConfigModifiedBefore = getMostRecentlyModified(configOutputDirPath, getMostRecentlyModified(configOutputLockFilePath, null));
} catch (Throwable t) {
return error(2, t.toString());
}
} else if (traceOutputFile != null) {
try {
Path path = Paths.get(transformPath(traceOutputFile));
TraceFileWriter writer = new TraceFileWriter(path);
tracer = writer;
tracingResultWriter = writer;
} catch (Throwable t) {
return error(2, t.toString());
}
}
if (build) {
int status = buildImage(jvmti);
if (status == 0) {
System.exit(status);
}
return status;
}
try {
BreakpointInterceptor.onLoad(jvmti, callbacks, tracer, this, interceptedStateSupplier, experimentalClassLoaderSupport, experimentalClassDefineSupport, trackReflectionMetadata);
} catch (Throwable t) {
return error(3, t.toString());
}
try {
JniCallInterceptor.onLoad(tracer, this, interceptedStateSupplier);
} catch (Throwable t) {
return error(4, t.toString());
}
setupExecutorServiceForPeriodicConfigurationCapture(configWritePeriod, configWritePeriodInitialDelay);
return 0;
}
use of com.oracle.svm.configure.filters.RuleNode in project graal by oracle.
the class ConfigurationTool method addSingleRule.
private static RuleNode addSingleRule(RuleNode rootNode, String argName, String qualifiedPkg, RuleNode.Inclusion inclusion) {
RuleNode root = maybeCreateRootNode(rootNode);
if (qualifiedPkg == null || qualifiedPkg.isEmpty()) {
throw new UsageException("Argument must be provided for: " + argName);
}
if (qualifiedPkg.indexOf('*') != -1 && !qualifiedPkg.endsWith(".**") && !qualifiedPkg.endsWith(".*")) {
throw new UsageException("Rule may only contain '*' at the end, either as .* to include all classes in the package, " + "or as .** to include all classes in the package and all of its subpackages");
}
root.addOrGetChildren(qualifiedPkg, inclusion);
return root;
}
use of com.oracle.svm.configure.filters.RuleNode in project graal by oracle.
the class ConfigurationTool method generate.
@SuppressWarnings("fallthrough")
private static void generate(Iterator<String> argsIter, boolean acceptTraceFileArgs) throws IOException {
List<URI> traceInputs = new ArrayList<>();
boolean builtinCallerFilter = true;
boolean builtinHeuristicFilter = true;
List<URI> callerFilters = new ArrayList<>();
ConfigurationSet omittedInputSet = new ConfigurationSet();
ConfigurationSet inputSet = new ConfigurationSet();
ConfigurationSet outputSet = new ConfigurationSet();
while (argsIter.hasNext()) {
String[] parts = argsIter.next().split("=", 2);
String current = parts[0];
String value = (parts.length > 1) ? parts[1] : null;
ConfigurationSet set = outputSet;
switch(current) {
case "--input-dir":
inputSet.addDirectory(requirePath(current, value));
break;
case "--output-dir":
Path directory = requirePath(current, value);
if (!Files.exists(directory)) {
Files.createDirectory(directory);
} else if (!Files.isDirectory(directory)) {
throw new NoSuchFileException(value);
}
outputSet.addDirectory(directory);
break;
case "--omit-from-input-dir":
omittedInputSet.addDirectory(requirePath(current, value));
break;
case "--reflect-input":
// fall through
set = inputSet;
case "--reflect-output":
set.getReflectConfigPaths().add(requirePathUri(current, value));
break;
case "--jni-input":
// fall through
set = inputSet;
case "--jni-output":
set.getJniConfigPaths().add(requirePathUri(current, value));
break;
case "--proxy-input":
// fall through
set = inputSet;
case "--proxy-output":
set.getProxyConfigPaths().add(requirePathUri(current, value));
break;
case "--resource-input":
// fall through
set = inputSet;
case "--resource-output":
set.getResourceConfigPaths().add(requirePathUri(current, value));
break;
case "--serialization-input":
// fall through
set = inputSet;
case "--serialization-output":
set.getSerializationConfigPaths().add(requirePathUri(current, value));
break;
case "--predefined-classes-input":
// fall through
set = inputSet;
case "--predefined-classes-output":
set.getPredefinedClassesConfigPaths().add(requirePathUri(current, value));
break;
case "--trace-input":
traceInputs.add(requirePathUri(current, value));
break;
case // legacy
"--no-filter":
builtinCallerFilter = false;
builtinHeuristicFilter = false;
break;
case "--no-builtin-caller-filter":
builtinCallerFilter = false;
break;
case "--no-builtin-heuristic-filter":
builtinHeuristicFilter = false;
break;
case "--caller-filter-file":
callerFilters.add(requirePathUri(current, value));
break;
case "--":
if (acceptTraceFileArgs) {
argsIter.forEachRemaining(arg -> traceInputs.add(Paths.get(arg).toUri()));
} else {
throw new UsageException("Unknown argument: " + current);
}
break;
default:
if (!acceptTraceFileArgs || current.startsWith("-")) {
throw new UsageException("Unknown argument: " + current);
}
traceInputs.add(Paths.get(current).toUri());
break;
}
}
failIfAgentLockFilesPresent(inputSet, omittedInputSet, outputSet);
RuleNode callersFilter = null;
if (!builtinCallerFilter) {
callersFilter = RuleNode.createRoot();
callersFilter.addOrGetChildren("**", RuleNode.Inclusion.Include);
}
if (!callerFilters.isEmpty()) {
if (callersFilter == null) {
callersFilter = AccessAdvisor.copyBuiltinCallerFilterTree();
}
for (URI uri : callerFilters) {
try {
FilterConfigurationParser parser = new FilterConfigurationParser(callersFilter);
parser.parseAndRegister(uri);
} catch (Exception e) {
throw new UsageException("Cannot parse filter file " + uri + ": " + e);
}
}
callersFilter.removeRedundantNodes();
}
AccessAdvisor advisor = new AccessAdvisor();
advisor.setHeuristicsEnabled(builtinHeuristicFilter);
if (callersFilter != null) {
advisor.setCallerFilterTree(callersFilter);
}
TraceProcessor p;
TraceProcessor omittedInputTraceProcessor;
try {
omittedInputTraceProcessor = new TraceProcessor(advisor, omittedInputSet.loadJniConfig(ConfigurationSet.FAIL_ON_EXCEPTION), omittedInputSet.loadReflectConfig(ConfigurationSet.FAIL_ON_EXCEPTION), omittedInputSet.loadProxyConfig(ConfigurationSet.FAIL_ON_EXCEPTION), omittedInputSet.loadResourceConfig(ConfigurationSet.FAIL_ON_EXCEPTION), omittedInputSet.loadSerializationConfig(ConfigurationSet.FAIL_ON_EXCEPTION), omittedInputSet.loadPredefinedClassesConfig(null, null, ConfigurationSet.FAIL_ON_EXCEPTION), null);
List<Path> predefinedClassDestDirs = new ArrayList<>();
for (URI pathUri : outputSet.getPredefinedClassesConfigPaths()) {
Path subdir = Files.createDirectories(Paths.get(pathUri).getParent().resolve(ConfigurationFile.PREDEFINED_CLASSES_AGENT_EXTRACTED_SUBDIR));
subdir = Files.createDirectories(subdir);
predefinedClassDestDirs.add(subdir);
}
Predicate<String> shouldExcludeClassesWithHash = omittedInputTraceProcessor.getPredefinedClassesConfiguration()::containsClassWithHash;
p = new TraceProcessor(advisor, inputSet.loadJniConfig(ConfigurationSet.FAIL_ON_EXCEPTION), inputSet.loadReflectConfig(ConfigurationSet.FAIL_ON_EXCEPTION), inputSet.loadProxyConfig(ConfigurationSet.FAIL_ON_EXCEPTION), inputSet.loadResourceConfig(ConfigurationSet.FAIL_ON_EXCEPTION), inputSet.loadSerializationConfig(ConfigurationSet.FAIL_ON_EXCEPTION), inputSet.loadPredefinedClassesConfig(predefinedClassDestDirs.toArray(new Path[0]), shouldExcludeClassesWithHash, ConfigurationSet.FAIL_ON_EXCEPTION), omittedInputTraceProcessor);
} catch (IOException e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException(t);
}
if (traceInputs.isEmpty() && inputSet.isEmpty()) {
throw new UsageException("No inputs specified.");
}
for (URI uri : traceInputs) {
try (Reader reader = Files.newBufferedReader(Paths.get(uri))) {
p.process(reader);
}
}
if (outputSet.isEmpty()) {
System.err.println("Warning: no outputs specified, validating inputs only.");
}
for (URI uri : outputSet.getReflectConfigPaths()) {
try (JsonWriter writer = new JsonWriter(Paths.get(uri))) {
p.getReflectionConfiguration().printJson(writer);
}
}
for (URI uri : outputSet.getJniConfigPaths()) {
try (JsonWriter writer = new JsonWriter(Paths.get(uri))) {
p.getJniConfiguration().printJson(writer);
}
}
for (URI uri : outputSet.getProxyConfigPaths()) {
try (JsonWriter writer = new JsonWriter(Paths.get(uri))) {
p.getProxyConfiguration().printJson(writer);
}
}
for (URI uri : outputSet.getResourceConfigPaths()) {
try (JsonWriter writer = new JsonWriter(Paths.get(uri))) {
p.getResourceConfiguration().printJson(writer);
}
}
for (URI uri : outputSet.getSerializationConfigPaths()) {
try (JsonWriter writer = new JsonWriter(Paths.get(uri))) {
p.getSerializationConfiguration().printJson(writer);
}
}
for (URI uri : outputSet.getPredefinedClassesConfigPaths()) {
try (JsonWriter writer = new JsonWriter(Paths.get(uri))) {
p.getPredefinedClassesConfiguration().printJson(writer);
}
}
}
use of com.oracle.svm.configure.filters.RuleNode in project graal by oracle.
the class ConfigurationTool method generateFilterRules.
private static void generateFilterRules(Iterator<String> argsIter) throws IOException {
Path outputPath = null;
boolean reduce = false;
List<String> args = new ArrayList<>();
while (argsIter.hasNext()) {
String arg = argsIter.next();
if (arg.startsWith("--reduce")) {
String[] parts = arg.split("=", 2);
reduce = (parts.length < 2) || Boolean.parseBoolean(parts[1]);
} else {
args.add(arg);
}
}
RuleNode rootNode = null;
for (String arg : args) {
String[] parts = arg.split("=", 2);
String current = parts[0];
String value = (parts.length > 1) ? parts[1] : null;
switch(current) {
case "--include-packages-from-modules":
case "--exclude-packages-from-modules":
case "--exclude-unexported-packages-from-modules":
if (!ImageInfo.inImageCode()) {
if (rootNode != null) {
throw new UsageException(current + " must be specified before other rule-creating arguments");
}
String[] moduleNames = (value != null) ? value.split(",") : new String[0];
RuleNode.Inclusion exportedInclusion = current.startsWith("--include") ? RuleNode.Inclusion.Include : RuleNode.Inclusion.Exclude;
RuleNode.Inclusion unexportedInclusion = exportedInclusion;
RuleNode.Inclusion rootInclusion = exportedInclusion.invert();
if (current.equals("--exclude-unexported-packages-from-modules")) {
rootInclusion = RuleNode.Inclusion.Include;
exportedInclusion = RuleNode.Inclusion.Include;
unexportedInclusion = RuleNode.Inclusion.Exclude;
}
rootNode = ModuleFilterTools.generateFromModules(moduleNames, rootInclusion, exportedInclusion, unexportedInclusion, reduce);
} else {
throw new UsageException(current + " is currently not supported in the native-image build of this tool.");
}
break;
case "--input-file":
rootNode = maybeCreateRootNode(rootNode);
new FilterConfigurationParser(rootNode).parseAndRegister(requirePathUri(current, value));
break;
case "--output-file":
outputPath = requirePath(current, value);
break;
case "--include-classes":
rootNode = addSingleRule(rootNode, current, value, RuleNode.Inclusion.Include);
break;
case "--exclude-classes":
rootNode = addSingleRule(rootNode, current, value, RuleNode.Inclusion.Exclude);
break;
default:
throw new UsageException("Unknown argument: " + current);
}
}
// in case of no inputs
rootNode = maybeCreateRootNode(rootNode);
rootNode.removeRedundantNodes();
if (outputPath != null) {
try (FileOutputStream os = new FileOutputStream(outputPath.toFile())) {
rootNode.printJsonTree(os);
}
} else {
rootNode.printJsonTree(System.out);
}
}
Aggregations