I'm writing a native Java agent using JVMTI that goes over all the methods of all the loaded classes. Unfortunately many classes seem not yet prepared and therefore GetClassMethods returns JVMTI_ERROR_CLASS_NOT_PREPARED. I am registering a ClassPrepare event callback but that seem to be called only for very few classes. Simplified (minus all the error handling and deallocation) my code looks like this
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* jvm, char *options, void *reserved) {
jvmtiEnv *jvmti;
jint class_count;
jclass* classes;
jint method_count;
jmethodID* methods;
(*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_11);
(*jvmti)->GetLoadedClasses(jvmti, &class_count, &classes);
for (int i = 0; i < class_count; i++) {
jclass klass = classes[i];
// here a lot of time JVMTI_ERROR_CLASS_NOT_PREPARED is returned
jvmtiError err = (*jvmti)->GetClassMethods(jvmti, klass, &method_count, &methods);
}
The agent is attached dynamically to a running JVM using JCMD and the JVMTI.agent_load command. I did try to register a class prepare callback using:
jvmtiEventCallbacks callbacks;
(void)memset(&callbacks, 0, sizeof(callbacks));
callbacks.ClassPrepare = &callbackClassPrepare;
(*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint) sizeof(callbacks));
(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, (jthread) NULL);
But this ended only being called for very few classes.
How can I get the JVM to prepare the loaded classes so that GetClassMethods returns JVMTI_ERROR_NONE?
So far I have only tested with JDK 17.0.1 with Shenandoah GC.
This is a normal situation when some classes are loaded but not linked. You don't need to do anything to prepare classes manually - JVM does this automatically when needed. JVM Specification guarantees the classes is completely prepared before it is initialized. As soon as it happens, JVM TI ClassPrepare event is fired.
So in order to get all available jmethodIDs you'll need:
Iterate over all loaded classes, ignoring possible JVMTI_ERROR_CLASS_NOT_PREPARED.
Set ClassPrepare event callback and call GetClassMethods in it.
Related
I am working on a Java library that is a thin wrapper for the Windows Waveform Functions to play 24 bit audio through Java. (The JVM only supports 8bit and 16bit audio).
The paradigm in the Windows Waveform Functions is:
Create Header struct
call waveOutPrepareHeader on the Header.
Send header to sound card
Sound card plays asynchronously (which means the Header must stay in memory for the duration of the audio playing)
When the sound card is done playing, it sets a "Done" bit in the header
When the "done" bit is set, I have to call waveOutUnprepareHeader
Then I can remove the header from memory
Given that my Java library is going to be a thin wrapper for the native Waveform Functions, I have a class for the Header Pointer, so I can keep it in scope for as long as needed, pass it around as needed, and eventually call waveOutUnprepareHeader on it.
public class WaveHeader {
long waveHeaderPointer;
public WaveHeader(byte[] buffer) {
waveHeaderPointer = HWaveOut.createHeader(buffer, buffer.length);
}
}
The native code being called above on line 5 (HWaveOut.createHeader()) is:
JNIEXPORT jlong JNICALL Java_net_joshuad_waveformjni_HWaveOut_createHeader
(JNIEnv * env, jclass jclass, jbyteArray jbuffer, jint jBufferSize) {
char* buffer = new char[jBufferSize];
asCharArray(env, jbuffer, buffer);
WAVEHDR* headerOut = new WAVEHDR{ buffer, (DWORD)jBufferSize, 0, 0, 0, 0, 0, 0 };
std::cout << "[C++] Header out location: " << headerOut << std::endl;
return (jlong)headerOut;
}
As you can see, I allocate a WAVEHDR on the heap in C++.
It is my understanding that I am responsible for de-allocating the WAVEHDR when I am done with it -- that the Java Garbage Collector won't destroy it for me.
I initially considered putting the de-allocation code in finalize() in java, so that the C++ struct is always automatically de-allocated when the java object is garbage-collected in java, but according to this answer this method will cause memory leaks.
I then thought of using the compiler warnings for unclosed resources in the classes like InputStream to catch any mistakes I make, but even if I make WaveHeader Closable, I don't get the compiler warnings I'm used to if I don't call close().
Is there a good way to protect myself from accidental memory leaks here?
One solution is to create a pool of these WAVEHDR objects at startup and only allow the Java code to take objects from the pool and recycle them. Failure to return objects will result in an empty pool right after startup and a crash.
You are right, the compiler won't warn you about missing close(), but lint or similar static code analysis tool, will. At any rate, Closeable is the recommended way to go, and if you use it with try(), the language will be on your side. Still, it's a good practice to call close() from finalize() (unless you know that your JVM has the bug described by Steven M. Cherry).
By the way, he did not say that finalize() caused a memory leak; this was a heap corruption, something much worse; but this report is of 2008, so you have little chance to encounter this bug in production.
As for the specific case of WAVEHDR, I would suggest not to allocate it in C++ on heap, but rather to keep it all (with the buffer) allocated in Java as a direct ByteBuffer:
public class WaveHeader {
private ByteBuffer waveHeader;
private final static int PTR_LENGTH = 8; // 64-bit Windows
private final static int DWORD_LENGTH = 4;
private final static int WAVEHDR_LENGTH = 4*PTR_LENGTH + 4*DWORD_LENGTH;
private native static void init(ByteBuffer waveHeader);
public WaveHeader(int bufferLength) {
waveHeader = allocateDirect(bufferLength + WAVEHDR_LENGTH);
init(waveHeader);
}
}
JNIEXPORT void JNICALL Java_net_joshuad_waveformjni_WaveHeader_init(JNIEnv* env, jclass clazz, jobject byteBuffer) {
auto waveHeader = reinterpret_cast<WAVEHDR*>(env->GetDirectBufferAddress(byteBuffer));
jlong capacity = env->GetDirectBufferCapacity(byteBuffer);
waveHeader->lpData = reinterpret_cast<LPSTR>(waveHeader+1);
waveHeader->dwBufferLength = capacity-sizeof(WAVEHDR);
}
Now you don't care about close() or managing the memory of your C++ object: it is all managed by Java.
I am writing a plug-in for an existing application. Implementation language is C. However, the actual functionality is implemented in Java. For this reason, I am using Java Native Interface (JNI) to create a JVM instance from within C. I can find the appropriate Java class and create an instance. This is what the code looks like:
login(uintptr_t connection, const char* username, …) {
…
jmethodID constructor = (*env)->GetMethodID(env, ps->class, "<init>", "(JLjava/lang/String;)V");
jstring jusername = (*env)->NewStringUTF(env, username);
jobject instance = (*env)->NewObject(env, ps->class, constructor, connection, jusername);
Everything works just fine.
On Linux.
On Windows, it is a complete mess. As soon as I try to create an instance of the Java class, it throws a
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0813751f, pid=8, tid=0x00000009
. More details are written to a log file, but the stack trace is not helpful other than pointing to somewhere in the jvm.dll. Stepping through with a debugger has not been insightful. Note this is not the same as this question.
After days, I figured it out.
The constructor I am invoking expects a parameter. The type is long (Java) aka J (JNI Type Signature) aka jlong (corresponing C type). A C uintptr_t is compatible with a jlong.
On Linux, my uintptr_t is 8 bytes long, since I am in a amd64 environment with 64 bit applications. For Windows, the application was build in 32 bit. As a result uintptr_t is only 4 bytes long, but the JVM still expect a 8 byte jlong. However, NewObject is a variadic function, automatic promotion does not happen and type safety is not guaranteed.
login(uintptr_t connection, const char* username, …) {
…
jmethodID constructor = (*env)->GetMethodID(env, ps->class, "<init>", "(JLjava/lang/String;)V");
jstring jusername = (*env)->NewStringUTF(env, username);
jlong jconnection = connection;
jobject instance = (*env)->NewObject(env, ps->class, constructor, jconnection, jusername);
A simple cast to the correct type was the solution. I expect this pitfall to exist with CallVoidMethod or any of the Call*Method mentioned in the documentation, too.
If I comment out the method (*a)->CallVoidMethod(a, b, meth, "FROM JNI");
The app doesn't crash else it crashes.
Assuming that I haven't released the resource "jstr" which might have caused a memory leak then why does it happen only when CallVoidMethod() ?
What should I do to solve this issue?
Thanks to all who spend their time writing down a solution or reading through this
ERROR:
android.process.acore E/StrictMode﹕ A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
java.lang.Throwable: Explicit termination method 'close' not called
This is my native "C" code ...
#include "com_example_prabhu_helloworldnative_HelloWorld.h"
#include <jni.h>
#include <android/log.h>
#define LOG_TAG "testjni"
#define ALOG(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
JNIEXPORT jstring JNICALL Java_com_example_prabhu_helloworldnative_HelloWorld_HelloJNI(JNIEnv *a, jobject b)
{
//jstring jstr = (*a)->NewStringUTF(a, "This comes from jni....");
jclass clazz =(*a)->GetObjectClass(a, b);
jmethodID meth = (*a)->GetMethodID(a, b, "messageMe", "(Ljava/lang/String;)V");
__android_log_print(ANDROID_LOG_DEBUG, "LOG_TAG", "\n this is log messge \n");
ALOG("Hello World");
if(meth == 0)
{
return;
}
(*a)->CallVoidMethod(a, b, meth, "FROM JNI");
//(*a)->Release
ALOG("REACHING HERE");
return (*a)->NewStringUTF(a, "APXOR");
}
StrictMode only reports failures to release objects that are explicitly monitored by StrictMode. It doesn't fire because you fail to release a string from JNI. The object allocated at the point in the code indicated by the stack trace needs to be released with an explicit close() call before the last reference to it is dropped. If the object is discarded and finalized before it is closed, the system reports an error.
The method you're calling may or may not have anything to do with the failing object -- it may simply be doing allocations that cause the GC to happen sooner, so your app reports the error immediately rather than doing so later.
Including more of the logcat output in your question may be useful here. The strict mode code is typically configured to issue warnings... if your app is actually crashing the problem may not be related to the failure to close the resource.
One possible problem with your code is misuse of b. You're passing it to GetMethodID(), which takes a jclass as its second argument; since that apparently works, I would expect 'b' to be a class. This will be the case if the HelloJNI method were declared static in the Java code. (You call GetObjectClass(a,b), but ignore the result.) That being the case, passing 'b' as the second argument to CallVoidMethod() is probably wrong, and will lead to a failure.
Further, as noted in another answer, you cannot pass a C string or char* when a jstring is required. "FROM JNI" must be converted to a jstring with NewStringUTF. The compiler should warn about that.
With CheckJNI enabled I would expect a JNI error to be reported, which does kill the app.
I think you have to create Java string yourself:
...
jstring str = env->NewStringUTF("FROM JNI");
(*a)->CallVoidMethod(a, b, meth, str);
env->DeleteLocalRef(str);
...
i'm trying to wrap a c++ library for a specific usb device in JAVA.
the library supports Callback functions to inform the application about the attachment and detachment of usb device to PC.
the call back function must have a specific format like this:
DWORD callbackFunction(void *params);
so i implemented a function like this in JNI dll and want to call a function in Java wapper whenever this function is called.
the question is what JNIENV i should use for calling GetObjectClass, GetMethodID and CallVoidMethod from?
This is how I initialize my DLL. the "Set(AttachDetach)Callback" methods accept a callback function(first parameter) and a void* parameter(secondparameter) that will be passed to the function when module attach/detach is detected.
JNIEXPORT void JNICALL Java_MyPackage_MyClass_InitializeDLL
(JNIEnv *env, jobject obj, jobject callback)
{
// Storing callback object in global variable.
callBackObj = callback;
env->GetJavaVM(&jvm);
MyInstance = new MyClass();
MyInstance ->SetAttachCallback(AttachCallBack, &callBackObj);
MyInstance ->SetDetachCallback(DetachCallBack, &callBackObj);
// Testing!
jclass callBackCls = env->FindClass("MyPackage/MyClassCallBacks");
jmethodID mid = env->GetMethodID(callBackCls, "attach", "(B)V");
if (!mid)
return ; /* method not found */
//This call here works well
env->CallVoidMethod(callBackObj, mid, 5);
}
then i set a callback function in DLL for the USB device and it is successfully called when i attach the device.
The code i put in attach callback of the USB device is this:
DWORD CALLBACK AttachCallBack(CallbackParams* params)
{
JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
jclass callBackCls = env->FindClass("MyPackage/MyClassCallBacks");
jmethodID mid = env->GetMethodID(callBackCls, "attach", "(B)V");
if (!mid)
return -1; /* method not found */
// This call fails with an access violation Exception
env->CallVoidMethod(*((jobject *)(params->param)), mid, params->moduleIndex);
// This fails the same way too
env->CallVoidMethod(callBackObj, mid, 5);
jvm->DetachCurrentThread();
return 0;
}
Before i use AttachCurrentThread i wasn't able to use JNIENV pointer at all. but now any other use of this pointer is successful instead of the call to CallVoidMethod.
Do you see what is wrong here?
Let me add that MyPackage.MyClassCallBacks Is an interface that it's method is implemented in another calss namely "callBackClass"
You need to have a reference to the current JVM:
JavaVM *jvm;
You can add an initilizing method to the C++ backend, which obtains this reference when the program starts:
JNIEXPORT void JNICALL init(JNIEnv *env, jclass){
env->GetJavaVM(&jvm);
}
And when observing for USB attachemnt / detachment, you can obtain JNIEnv from this JavaVM like this:
JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
//your code here
jvm->DetachCurrentThread();
This is implemented so that every USB device change creates a new thread. If you use only one thread for the chceking, you need to attach only once (in the initilizer, perhaps?) and you will then have JNIEnv valid as long as your native thread stays attached to the JVM.
What you may have to do is create a queue in C and wait on it or poll it using a Java thread. This would always have a current JNIEnv available to it.
It seems you can't ...
.... save the JNIENV from the last JNI call which set this up and reuse it.
Your callback appears to return params which you may have passed it when you setup the call back. You can make one of them the JNIENV.
I had the same problem too. It was as though the reference to the object, created in the initialization method, was of no use in the other methods. And it is indeed like that.The solution is in the initialization of the reference to the object, which must be initialized not simply with
callBackObj = callback
but with
callbackObj = env->NewGlobalRef(callback)
Same issue here: Objective C calling Java methods using JNI
Create a JNI init(JNIEnv * env, jclass c (or jobject o) ) and
save param #1 JNIEnv
save param #2 jclass (if static)
or
save param #2 jobject (in non-static)
lookup and save the jmethodID(s) for the Java method(s) you will be invoking.
Good idea to also have a JNI shutdown(JNIEnv * env, jclass (or jobject) ) for native shutdown/cleanup
Is there a nice Java API I can use on top of JVMTI?
JVMTI was not built for having Java API on top. The JVM TI definition itself says:
The JVM tool interface (JVM TI) is a standard native API that allows for native libraries to capture events and control a Java Virtual Machine (JVM) for the Java platform.
Since it was built for native API to capture events & controls, I don't think there are API on top of it. May you can explain what you are trying to achieve?
I am not aware of any Java API on top of JVM TI.
ok... just tried it... seems to work as expected.... in real life the VMInit callback would return an instance of a class that implemented an interface that mirrored the C JVMTI interface.... the C agent would store this instance and call it when required on events .... additionally before the VMInit Java returned it would set up capabilities and callbacks and register events etc.... you would probably be able to get about 90% JVMTI API coverage..... it's just a case of typing it in .... I could do it in a weekend if you have a strong case :-)
the following code produces this:
C: VMInit, preparing to callback Java method
Java: JVMTI callback class, VMInit().
C: VMInit, callback Java method returned successfully
Java: And Finally... Hello, I'm the Java main
package com.stackoverflow;
public class JVMTICallback {
public static void VMInit() {
System.out.println("Java:\tJVMTI callback class, VMInit().");
}
public static void main(String[] args) {
// This main is only here to give us something to run for the test
System.out.println("Java:\tAnd Finally... Hello, I'm the Java main");
}
}
and the C
#include <stdlib.h>
#include "jvmti.h"
jvmtiEnv *globalJVMTIInterface;
void JNICALL
vmInit(jvmtiEnv * jvmti_env, JNIEnv * jni_env, jthread thread)
{
printf("C:\tVMInit, preparing to callback Java method\n");
char *className = "com/stackoverflow/JVMTICallback";
char *methodName = "VMInit";
char *descriptor = "()V";
jclass callbackClass = (*jni_env)->FindClass(jni_env, className);
if (!callbackClass) {
fprintf(stderr,"C:\tUnable to locate callback class.\n");
return;
}
jmethodID callbackMethodID = (*jni_env)->GetStaticMethodID(jni_env, callbackClass, methodName, descriptor);
if (!callbackMethodID)
{
fprintf(stderr, "C:\tUnable to locate callback VMInit method\n");
return;
}
(*jni_env)->CallStaticVoidMethodV(jni_env, callbackClass, callbackMethodID, NULL);
printf("C:\tVMInit, callback Java method returned successfully\n");
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM * jvm, char *options, void *reserved)
{
jint returnCode = (*jvm)->GetEnv(jvm, (void **) &globalJVMTIInterface,
JVMTI_VERSION_1_0);
if (returnCode != JNI_OK)
{
fprintf(stderr,
"The version of JVMTI requested (1.0) is not supported by this JVM.\n");
return JVMTI_ERROR_UNSUPPORTED_VERSION;
}
jvmtiEventCallbacks *eventCallbacks;
eventCallbacks = calloc(1, sizeof(jvmtiEventCallbacks));
if (!eventCallbacks)
{
fprintf(stderr, "Unable to allocate memory\n");
return JVMTI_ERROR_OUT_OF_MEMORY;
}
eventCallbacks->VMInit = &vmInit;
returnCode = (*globalJVMTIInterface)->SetEventCallbacks(globalJVMTIInterface,
eventCallbacks, (jint) sizeof(*eventCallbacks));
if (returnCode != JNI_OK)
{
fprintf(stderr, "C:\tJVM does not have the required capabilities (%d)\n",
returnCode);
exit(-1);
}
returnCode = (*globalJVMTIInterface)->SetEventNotificationMode(
globalJVMTIInterface, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, (jthread) NULL);
if (returnCode != JNI_OK)
{
fprintf(
stderr,
"C:\tJVM does not have the required capabilities, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT (%d)\n",
returnCode);
exit(-1);
}
return JVMTI_ERROR_NONE;
}
I searched around and unfortunately can't find any Java API library on top of JVMTI. Seems like you're out of luck.
What you can do though is to call a native lib from your Java code. I'm not very good at C/C++ but from JVMTI docs I see that it's possible to build a small shared library from provided headers. Then you can call it using JNA**. It will give you a nice API wrapper around native library.
Take a look at examples at JNA Getting Started page
This page also links to JNAerator which can generate all necessary Java bindings for you.
The downside of this approach is a necessity of maintaining this thin native layer for your target platforms.
** JNA deals a runtime overhead compared to usual JNI but ease of development overweights performance benefits IMO. Switch to JNI only if you have to.
It will not work. JVMTI has callbacks that the Java code has no direct control over (like ClassPrepare). If these callbacks are implemented in Java, the execution can lead other callbacks causing deadlock.
it wouldn't be difficult to write.... just thunk the JVMTI calls to callback a Java class over JNI.. you would probably face a couple of issues... firstly the Agent_onLoad.. this initial "registering" function happens too early on in the lifecycle of the JVM for it to callback your java.... secondly there are potential circularity issues and the probability that the JVM was written expecting you to do anything like this this at...
Iĺl try to write an example.... back in a few mins...
JDI is a TOP level interface written in Java, which uses JVMTI as the backend api.
this link give you detailed info.