use of com.oracle.svm.agent.stackaccess.InterceptedState in project graal by oracle.
the class JniCallInterceptor method getFieldID.
@CEntryPoint(name = "GetFieldID")
@CEntryPointOptions(prologue = AgentIsolate.Prologue.class)
private static JNIFieldId getFieldID(JNIEnvironment env, JNIObjectHandle clazz, CCharPointer name, CCharPointer signature) {
InterceptedState state = initInterceptedState();
JNIObjectHandle callerClass = getCallerClass(state, env);
JNIFieldId result = jniFunctions().getGetFieldID().invoke(env, clazz, name, signature);
if (shouldTrace()) {
traceCall(env, "GetFieldID", clazz, getFieldDeclaringClass(clazz, result), callerClass, result.isNonNull(), state, fromCString(name), fromCString(signature));
}
return result;
}
use of com.oracle.svm.agent.stackaccess.InterceptedState 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.agent.stackaccess.InterceptedState in project graal by oracle.
the class BreakpointInterceptor method onClassFileLoadHook.
@CEntryPoint
@CEntryPointOptions(prologue = AgentIsolate.Prologue.class)
private static void onClassFileLoadHook(@SuppressWarnings("unused") JvmtiEnv jvmti, JNIEnvironment jni, @SuppressWarnings("unused") JNIObjectHandle classBeingRedefined, JNIObjectHandle loader, CCharPointer name, @SuppressWarnings("unused") JNIObjectHandle protectionDomain, int classDataLen, CCharPointer classData, @SuppressWarnings("unused") CIntPointer newClassDataLen, @SuppressWarnings("unused") CCharPointerPointer newClassData) {
InterceptedState state = interceptedStateSupplier.get();
if (loader.equal(nullHandle())) {
// boot class loader
return;
}
String className = fromCString(name);
if (className != null && AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(className).matches()) {
// Proxy classes are handled using a different mechanism, so we ignore them here
return;
}
for (JNIObjectHandle builtinLoader : builtinClassLoaders) {
if (jniFunctions().getIsSameObject().invoke(jni, loader, builtinLoader)) {
return;
}
}
if (jniFunctions().getIsInstanceOf().invoke(jni, loader, agent.handles().jdkInternalReflectDelegatingClassLoader)) {
return;
}
byte[] data = new byte[classDataLen];
CTypeConversion.asByteBuffer(classData, classDataLen).get(data);
tracer.traceCall("classloading", "onClassFileLoadHook", null, null, null, null, state.getFullStackTraceOrNull(), className, data);
}
use of com.oracle.svm.agent.stackaccess.InterceptedState in project graal by oracle.
the class BreakpointInterceptor method onBreakpoint.
@CEntryPoint
@CEntryPointOptions(prologue = AgentIsolate.Prologue.class)
private static void onBreakpoint(@SuppressWarnings("unused") JvmtiEnv jvmti, JNIEnvironment jni, @SuppressWarnings("unused") JNIObjectHandle thread, JNIMethodId method, @SuppressWarnings("unused") long location) {
if (recursive.get()) {
return;
}
recursive.set(true);
try {
Breakpoint bp = installedBreakpoints.get(method.rawValue());
InterceptedState state = interceptedStateSupplier.get();
if (bp.specification.handler.dispatch(jni, bp, state)) {
guarantee(!testException(jni));
}
} catch (Throwable t) {
VMError.shouldNotReachHere(t);
} finally {
recursive.set(false);
}
}
use of com.oracle.svm.agent.stackaccess.InterceptedState in project graal by oracle.
the class JniCallInterceptor method getStaticFieldID.
@CEntryPoint(name = "GetStaticFieldID")
@CEntryPointOptions(prologue = AgentIsolate.Prologue.class)
private static JNIFieldId getStaticFieldID(JNIEnvironment env, JNIObjectHandle clazz, CCharPointer name, CCharPointer signature) {
InterceptedState state = initInterceptedState();
JNIObjectHandle callerClass = getCallerClass(state, env);
JNIFieldId result = jniFunctions().getGetStaticFieldID().invoke(env, clazz, name, signature);
if (shouldTrace()) {
traceCall(env, "GetStaticFieldID", clazz, getFieldDeclaringClass(clazz, result), callerClass, result.isNonNull(), state, fromCString(name), fromCString(signature));
}
return result;
}
Aggregations