use of com.oracle.svm.configure.trace.AccessAdvisor 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.trace.AccessAdvisor in project graal by oracle.
the class TypeMethodsWithFlagsTest method loadTraceProcessorFromResourceDirectory.
private static TraceProcessor loadTraceProcessorFromResourceDirectory(String resourceDirectory, TraceProcessor previous) {
try {
ConfigurationSet configurationSet = new ConfigurationSet();
configurationSet.addDirectory(resourceFileName -> {
try {
String resourceName = resourceDirectory + "/" + resourceFileName;
URL resourceURL = OmitPreviousConfigTests.class.getResource(resourceName);
return (resourceURL != null) ? resourceURL.toURI() : null;
} catch (Exception e) {
throw VMError.shouldNotReachHere("Unexpected error while locating the configuration files.", e);
}
});
AccessAdvisor unusedAdvisor = new AccessAdvisor();
Function<IOException, Exception> handler = e -> {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
Assert.fail("Exception occurred while loading configuration: " + e + System.lineSeparator() + sw);
return e;
};
Predicate<String> shouldExcludeClassesWithHash = null;
if (previous != null) {
shouldExcludeClassesWithHash = previous.getPredefinedClassesConfiguration()::containsClassWithHash;
}
return new TraceProcessor(unusedAdvisor, configurationSet.loadJniConfig(handler), configurationSet.loadReflectConfig(handler), configurationSet.loadProxyConfig(handler), configurationSet.loadResourceConfig(handler), configurationSet.loadSerializationConfig(handler), configurationSet.loadPredefinedClassesConfig(null, shouldExcludeClassesWithHash, handler), previous);
} catch (Exception e) {
throw VMError.shouldNotReachHere("Unexpected error while loading the configuration files.", e);
}
}
use of com.oracle.svm.configure.trace.AccessAdvisor 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.trace.AccessAdvisor in project graal by oracle.
the class NativeImageAgent method createAccessAdvisor.
private static AccessAdvisor createAccessAdvisor(boolean builtinHeuristicFilter, RuleNode callerFilter, RuleNode accessFilter) {
AccessAdvisor advisor = new AccessAdvisor();
advisor.setHeuristicsEnabled(builtinHeuristicFilter);
if (callerFilter != null) {
advisor.setCallerFilterTree(callerFilter);
}
if (accessFilter != null) {
advisor.setAccessFilterTree(accessFilter);
}
return advisor;
}
Aggregations