With JVM TI and java.lang.instrument we now have rich
support for instrumenting Java programming language
methods. But what about native methods?
The NativeMethodBind event allows us to interpose a native
function around the implementation of a native method. This
works well for known native methods which we want to
intercept -- since we can hand craft the interposer.
However, for arbitrary native methods it would require
generating native code on the fly that could do arbitrary
argument processing. This would be an implementation and
testing nightmare, and worse it is platform specific.
We need an approach which avoids the above. Interposing
at the Java programming language level would achieve this.
It also has the advantage that it is at the same level as
Java programming language method instrumentation.
For example, if we had:
native boolean foo(int x);
We could transform the class file (at load time, since
this transformation isn't allowed in a redefine) so that
this becomes:
boolean foo(int x) {
... record entry to foo ...
return wrapper_foo(x);
}
native boolean wrapped_foo(int x);
Where foo becomes a wrapper for the actual native method
with the appended prefix "wrapped_". Note that
"wrapped_" would be a bad prefix and that something like
"$$$MyAgentWrapped$$$_" would be better but would make
these examples less readable.
But now the problem is linking up the wrapped method with
the native implementation. That is, the method
wrapped_foo needs to be resolved to the native
implementation of foo, e.g.:
Java_somePackage_someClass_foo(JNIEnv* env, jint x)
There are two ways that resolution occurs, explicit
resolution with the JNI function RegisterNatives and
the normal automatic resolution. For RegisterNatives, it
will attempt this association:
method(foo) -> nativeImplementation(foo)
This needs to be changed to:
method(wrapped_foo) -> nativeImplementation(foo)
For automatic resolution, it will attempt:
method(wrapped_foo) -> nativeImplementation(wrapped_foo)
This needs to be changed to:
method(wrapped_foo) -> nativeImplementation(foo)
Possible solutions:
(1) Build some prefix into the VM to try on failure.
Besides being a hack, this won't work if there are
multiple agents wrapping native methods.
(2) For RegisterNatives the JNI interception functionality
could be used to change the arguments.
Yes, but there are order of wrapper application issues
for multiple agents.
(3) Provide events on native resolution failure.
This will work. But to work in a multiple agent case
the events would be complex and very specifically tied
to this application. For example: the event on
automatic resolution failure would have to be called
in the reverse environment ordering from standard event
processing in order the remove the last prefix first,
both events would need to have before and after strings,
and each agent getting the RegisterNatives case would
have to check intermediate links in the wrapper chain.
(4) Each environment can specify a native method prefix.
This is what I propose. It is simple and easy to use.
It is uncomfortably specific to this application, but
per the event solution above, this is unavoidable.
The JVM TI function could simply be:
jvmtiError SetNativeMethodPrefix(jvmtiEnv* env,
char* prefix);
What about java.lang.instrument, it would have the analog:
void setNativeMethodPrefix(ClassFileTransformer transformer,
String prefix);
But the JVM TI agent that implements java.lang.instrument
(at least in the RI) is a meta agent, that is, there is
one JVM TI environment no matter how many
java.lang.instrument agents there are. So, the JVM TI
function will need to be:
jvmtiError SetNativeMethodPrefixes(jvmtiEnv* env,
jint prefixCount,
char** prefixes);
###@###.### 2005-04-29 01:13:37 GMT
support for instrumenting Java programming language
methods. But what about native methods?
The NativeMethodBind event allows us to interpose a native
function around the implementation of a native method. This
works well for known native methods which we want to
intercept -- since we can hand craft the interposer.
However, for arbitrary native methods it would require
generating native code on the fly that could do arbitrary
argument processing. This would be an implementation and
testing nightmare, and worse it is platform specific.
We need an approach which avoids the above. Interposing
at the Java programming language level would achieve this.
It also has the advantage that it is at the same level as
Java programming language method instrumentation.
For example, if we had:
native boolean foo(int x);
We could transform the class file (at load time, since
this transformation isn't allowed in a redefine) so that
this becomes:
boolean foo(int x) {
... record entry to foo ...
return wrapper_foo(x);
}
native boolean wrapped_foo(int x);
Where foo becomes a wrapper for the actual native method
with the appended prefix "wrapped_". Note that
"wrapped_" would be a bad prefix and that something like
"$$$MyAgentWrapped$$$_" would be better but would make
these examples less readable.
But now the problem is linking up the wrapped method with
the native implementation. That is, the method
wrapped_foo needs to be resolved to the native
implementation of foo, e.g.:
Java_somePackage_someClass_foo(JNIEnv* env, jint x)
There are two ways that resolution occurs, explicit
resolution with the JNI function RegisterNatives and
the normal automatic resolution. For RegisterNatives, it
will attempt this association:
method(foo) -> nativeImplementation(foo)
This needs to be changed to:
method(wrapped_foo) -> nativeImplementation(foo)
For automatic resolution, it will attempt:
method(wrapped_foo) -> nativeImplementation(wrapped_foo)
This needs to be changed to:
method(wrapped_foo) -> nativeImplementation(foo)
Possible solutions:
(1) Build some prefix into the VM to try on failure.
Besides being a hack, this won't work if there are
multiple agents wrapping native methods.
(2) For RegisterNatives the JNI interception functionality
could be used to change the arguments.
Yes, but there are order of wrapper application issues
for multiple agents.
(3) Provide events on native resolution failure.
This will work. But to work in a multiple agent case
the events would be complex and very specifically tied
to this application. For example: the event on
automatic resolution failure would have to be called
in the reverse environment ordering from standard event
processing in order the remove the last prefix first,
both events would need to have before and after strings,
and each agent getting the RegisterNatives case would
have to check intermediate links in the wrapper chain.
(4) Each environment can specify a native method prefix.
This is what I propose. It is simple and easy to use.
It is uncomfortably specific to this application, but
per the event solution above, this is unavoidable.
The JVM TI function could simply be:
jvmtiError SetNativeMethodPrefix(jvmtiEnv* env,
char* prefix);
What about java.lang.instrument, it would have the analog:
void setNativeMethodPrefix(ClassFileTransformer transformer,
String prefix);
But the JVM TI agent that implements java.lang.instrument
(at least in the RI) is a meta agent, that is, there is
one JVM TI environment no matter how many
java.lang.instrument agents there are. So, the JVM TI
function will need to be:
jvmtiError SetNativeMethodPrefixes(jvmtiEnv* env,
jint prefixCount,
char** prefixes);
###@###.### 2005-04-29 01:13:37 GMT
- relates to
-
JDK-6263319 java.lang.instrument: Need the ability to instrument native methods
-
- Resolved
-