I've been fiddling with JVMTI API, trying to write something useful. Pasting code from other SO questions together, I've attempted to create a simple tool which would dump the heap when some particular kind of exception occurs, possibly useful in environments where creating jdb listener would be unwieldy. Mentioned code:
#include <jni.h>
#include <jvmti.h>
#include <string.h>
#include <stdio.h>
#include "jmm.h"
JNIEXPORT void* JNICALL JVM_GetManagement(jint version);
jvmtiEnv* jvmti;
void JNICALL
Exception(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location,
jobject exception,
jmethodID catch_method,
jlocation catch_location) {
char *exception_signature;
/* Obtain signature of the exception and compare type name with FNFE */
jclass class = (*jni_env)->GetObjectClass(jni_env, exception);
(*jvmti)->GetClassSignature(jvmti_env, class, &exception_signature, NULL);
if (strcmp("Ljava/io/FileNotFoundException;", exception_signature)==0) {
JmmInterface* jmm = (JmmInterface*) JVM_GetManagement(JMM_VERSION_1_0);
if (jmm == NULL) {
printf("Sorry, JMM is not supported\n");
} else {
jstring path = (*jni)->NewStringUTF(jni, "dump.hprof");
jmm->DumpHeap0(jni, path, JNI_TRUE);
printf("Heap dumped\n");
}
}
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
(*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0);
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = Exception;
(*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, NULL)
return 0;
}
The process as such is working; I get a heap dump while trying this code on a simple test program (which prints a few lines and generates exception between them). The problem is, thread stack analysis in Eclipse MAT shows me stack trace with DestroyJavaVM instead of main method, unlike heap dump generated with jmap. I expected that heap dump to be different from the jmap one, as it's a JNI call 'interrupting' the normal execution of the instrumented thread, but not disrupting the whole trace, just with native calls lying atop the stack.
Why is that so, and can I somehow prevent the issue, and dump the heap the way I'd like it to? Or is my entire reasoning wrong and it's not possible?
Related
I'm trying to understand the lifecycle stages of an Android app with a native lib.
The native library is loaded like this in the MainActivity:
static {
System.loadLibrary("lifecycleevents");
}
The definition of JNI_onLoad and JNI_onUnLoad are as follows:
extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad (JavaVM * pJavaVM, [[maybe_unused]] void * pReserved) {
JNIEnv * env;
if (pJavaVM->GetEnv ((void **) &env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
__android_log_print (ANDROID_LOG_VERBOSE, (const char *) TAG, "JNI_OnLoad");
return env->GetVersion ();
}
extern "C" JNIEXPORT void JNICALL
JNI_OnUnload (JavaVM * pJavaVM, [[maybe_unused]] void * pReserved) {
__android_log_print (ANDROID_LOG_VERBOSE, (const char *) TAG, "JNI_OnUnload");
}
I'm able to trace JNI_onLoad but JNI_onUnLoad never hits.
According to the documentation, it will be called when the classloader of the native lib is garbage collected.
This will happen at some point after MainActivity's onDestroy but before the app terminates, right? So, why is JNI_onUnLoad not called?
Also, in the documentation, JNI_onUnLoad_L will be called when the classloader of the native lib L is garbage collected. Since my native lib name is lifecycleevents, I renamed the JNI_onUnLoad method to JNI_onUnLoad_lifecycleevents, but it still doesn't get called.
I'm confused. Are both the methods valid? Requesting help from anyone who is familiar with this topic.
I had wrote a agent to use the JVMTI on android 9. Code like this, I create an AgentFunction object to monitor the VM. It work's fine.
AgentFunction *agent = 0;
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
agent = new AgentFunction();
agent->Init(vm);
agent->ParseOptions(options);
agent->AddCapability();
agent->ShowCapabilities();
agent->RegisterEvent();
return JNI_OK;
}
Then i want export some interface to java, than user can invoke the JVMTI function directly.
private native boolean applyChangeNative(List<ClassInfo> classes);
The JNI fumction in agent.so
extern "C"
JNIEXPORT jboolean JNICALL Java_com_cc_jvmtiagent_JVMTIFunction_applyChangeNative
(JNIEnv *jniEnv, jobject, jlong jvmti, jobject classInfo) {
...
jvmtiClassDefinition *def = new jvmtiClassDefinition[total_classes];
agent->RedefineClasses(total_classes, def);
}
But when invoke the native method applyChangeNative from JAVA, the agent->RedefineClasses crash caused by agent is null. After my test, i found i can't access the object create in JVMTI from JNI.
I had read the JDK souce code , I found it have an InvocationAdapter.cc, When Agent_OnAttach it create the JPLISAgent, then create java.lang.instrument.Instrumentation and save the JPLISAgent in it. Each function from Java will take the JPLISAgent point.
But i want to known , why access the JVMTI object is NULL directly from JNI?
Resolved:
If you want invoke the agent method via JNI, you should use System.Load(agentPath) instead of System.LoadLibrary(libName). It need use the same so file.
It work's on Android 9 and 10, But on Android 8.x, Can't access the agent, i do not know why.
I am building a small app on a raspberry pi.
I have a JVM which tries to access a C++ Library called "RCSwitch"
I created a JavaClass
public class NativeRCSwitchAdapter {
private static final NativeRCSwitchAdapter instance = new NativeRCSwitchAdapter();
public static NativeRCSwitchAdapter getInstance(){
return instance;
}
private NativeRCSwitchAdapter(){};
static{
String path = NativeRCSwitchAdapter.class.getProtectionDomain().getCodeSource().getLocation().getPath();
System.load(path + "NativeRCSwitchAdapter.so");
}
// methods to redirect to native layer (C++)
public native void switchOn(String group, String channel);
public native void switchOff(String group, String channel);
}
I then ran javac & javah to have java generate my header file for me.
I created a c++ file:
#include "NativeRCSwitchAdapter.h"
#include "RCSwitch.h"
#include <stdio.h>
#include <iostream>
using namespace std;
JNIEXPORT void JNICALL Java_NativeRCSwitchAdapter_switchOn(JNIEnv * env, jobject obj, jstring jsGroup, jstring jsChannel ){
cout<<"teststring output"<<endl;
const char *csGroup = env->GetStringUTFChars(jsGroup, 0);
const char *csChannel = env->GetStringUTFChars(jsChannel, 0);
char sGroup[6];
char sChannel[6];
for (int i = 0; i<5; i++) {
sGroup[i] = csGroup[i];
sChannel[i] = csChannel[i];
}
sGroup[5] = '\0';
sChannel[5] = '\0';
cout<<"ONON"<<endl;
cout<<sGroup<<endl;
cout<<sChannel<<endl;
RCSwitch mySwitch = RCSwitch();
//for testing purposes set to the ELRO Power Plugs
mySwitch.setPulseLength(300);
mySwitch.enableTransmit(0);
mySwitch.setRepeatTransmit(3);
mySwitch.switchOn(sGroup, sChannel);
}
Now this file uses the RCSwitch library which in turn uses the wiringPi library.
Now if i compile i run this:
g++ -shared -I/usr/jdk1.8.0/include -I/usr/jdk1.8.0/include/linux NativeRCSwitchAdapter.cpp -o NativeRCSwitchAdapter.so
Yet I get this error if start everything from java: (simple main, create an instance of my object and run the switchOn()
java: symbol lookup error: /home/pi/applications/Pi-jAutomation433/RCSwitchJNIWrapper/src/NativeRCSwitchAdapter.so: undefined symbol: _ZN8RCSwitchC1Ev
It has been time, since i last coded in C, so please forgive me but I believe it has something to do with the the linking phase of the compiler? Or does the compiler automatically check all dependencies and then their deps until no further dependencies are found and it then links it all nicely together and wraps it in an app?
Oh here is the repo to have an in depth look if anybody cares:
Github repo
Thanks for any help coming my way!
UPDATE
Okay so I managed to get this error away. Turns out (well I kinda knew that already but yeah) I am quiet a duphus when it comes to C++ compiler knowledge. Anyways I managed to get the error changed. I didn't know I had to explicitly tell g++ to include RCSwitch.cpp as well. Okay so now I did. Next error ;-)
I guess this time it should be fairly easy to tackle. I get an undefined symbol "pinMode".
This symbol is part of the wiringPi library. Do I have to include ALL c librarys that are executed in my java file? Or only the one I access and anything after that doesnt matter to java?
Your native function declaration is getting mangled by the c++ compiler. Add extern "C" around your declarations to clear up the issue.
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
JNIEXPORT void JNICALL Java_NativeRCSwitchAdapter_switchOn(JNIEnv * env, jobject obj, jstring jsGroup, jstring jsChannel ){
#ifdef __cplusplus
}
#endif /* __cplusplus */
Edit:
You need to include all other objects/libraries into your creation of the shared library.
See this Dynamic Link Library Q/A.
Good morning,
i need to obtain the value of the CPUs high-resolution performance counter in order to measure delay between various software applications. In my c(++|#) i use the
BOOL WINAPI QueryPerformanceCounter(
__out LARGE_INTEGER *lpPerformanceCount
);
WinApi call. What is the proper way to obtain the counter from java. I have search jna without success. I know this is a platform specific issue, but maybe there is a quicker way than writing my own jni wrapper?
Best wishes,
Armin
How about using System.nanoTime? I think that already uses the performance counters of the machine and there is no need to write a native wrapper.
Update: According to this article on clocks and timers in the jvm in the section "Clocks and Timers on Windows"
System.nanoTime() is implemented using the QueryPerformanceCounter/QueryPerformanceFrequency API
Here is the native wrapper code
1) File W32Call.java
package jmSense.Native; public class W32Call {
public native static long QueryPerformanceCounter( );
public native static int QueryPerformanceCounterInt32( );
2) run java h to create the include file
3) Create a dll "My Native Extensions.dll" from
#include "stdafx.h"
#include "windows.h"
#include "jmSense_Native_W32Call.h"
JNIEXPORT jlong JNICALL Java_jmSense_Native_W32Call_QueryPerformanceCounter(JNIEnv *, jclass)
{
LARGE_INTEGER g_CurentCount;
QueryPerformanceCounter((LARGE_INTEGER*)&g_CurentCount);
return g_CurentCount.QuadPart;
}
JNIEXPORT jint JNICALL Java_jmSense_Native_W32Call_QueryPerformanceCounterInt32(JNIEnv *, jclass)
{
LARGE_INTEGER g_CurentCount;
QueryPerformanceCounter((LARGE_INTEGER*)&g_CurentCount);
return g_CurentCount.LowPart;
}
4) Use it like this:
System.loadLibrary("My Native Extensions");
System.out.println(W32Call.QueryPerformanceCounter());
fine
I'm trying to do the following (not sure if I'm missing something or if this is not possible):
I have a Java class (in this particular case a Servlet) that calls a native method.
In this native method I'm spawning a new thread, and in that thread I would like to call a method on that Java object. Is that even possible?
What I tried in the native method that is called (original thread) is to remember the JavaVM instance, so that I can later can attach the other thread to it (seems to work), and make a NewGlobal ref for the jobject:
JavaVM *vm;
env->GetJavaVM(&vm);
env->NewGlobalRef(jobj)
What I don't know is how to retrieve the jobject in the other thread. If I just pass it the VM crashes, I assume because of an illegal thread access. If I create a new object for the class, I wouldn't have the exact object that I need.
Any ideas?
Thank you,
Mark
SOME ADDITIONAL CODE (method names etc. obscured):
The method that is called from the servlet:
JNIEXPORT jstring JNICALL ABC (JNIEnv *env, jobject jobj, some more arguments
{
JavaVM *vm;
env->GetJavaVM(&vm);
env->NewGlobalRef(jobj);
// spawning new thread (I'm using boost libraries)
boost::thread t = boost::thread(boost::bind(&XYZ::DEF, instance of XYZ, vm, &jobj);
...
}
void XYZ::DEF(JavaVM* vm, jobject* jobj)
{
JNIEnv* env;
vm->GetEnv( (void**)&env, JNI_VERSION_1_2);
vm->AttachCurrentThread((void**)&env, NULL);
... then eventually calling another method, but still the same thread, where I'm doing this:
jobject bb = env->NewDirectByteBuffer(...,...); // this crashed when I just used the env from the original thread, seems to work since I'm attaching the second thread to the VM.
// it crashes somewhere during the following code:
jclass cls = env->GetObjectClass(jobj);
jmethodID mid = env->GetMethodID(cls, "callback", "(I)V");
env->CallVoidMethod(jobj, mid, 13);
The crash produces something like this "A fatal error has been detected by the JRE... Problematic frame: V [libjvm.so+0x3e9220]...
You seem to be ignoring the result of NewGlobalRef. You have to use its result in the other thread instead of the original jobj.