I am using the Android NDK on a Motorola G6, running Android 8.0.
I am using JNI to perform periodic callbacks to my Java code in the following:
if ( data_arg && g_vm && g_obj ) {
jint res = (*g_vm)->GetEnv(g_vm, (void**)&g_env, JNI_VERSION_1_6);
if (res != JNI_OK) {
res = (*g_vm)->AttachCurrentThread(g_vm, &g_env, NULL);
if (JNI_OK != res) {
return NULL;
}
jclass interfaceClass = (*g_env)->GetObjectClass(g_env,g_obj);
if (!interfaceClass) {
return NULL;
}
g_mid = (*g_env)->GetMethodID(g_env,interfaceClass, "readCallback", "(I[B)V");
if (!g_mid){
return NULL;
}
jbyteArray dataByteArray = (*g_env) -> NewByteArray(g_env,data_arg->len);
(*g_env)->SetByteArrayRegion(g_env, dataByteArray, 0,data_arg->len,(jbyte*)data_arg->data);
(*g_env)->CallVoidMethod(g_env, g_obj,(jmethodID)g_mid, (jint)data_arg->len, (jbyteArray)dataByteArray);
(*g_env)->DeleteLocalRef(g_env,dataByteArray);
(*g_env)->DeleteLocalRef(g_env,res);
(*g_env)->DeleteLocalRef(g_env,interfaceClass);
}
The g_vm and g_obj variables are initialized when the app starts up with the following:
JNIEXPORT jint JNICALL Java_com_abc_register(JNIEnv *env, jobject obj) {
int returnValueBool = 1;
(*env)->GetJavaVM(env, &g_vm);
g_obj = (*env)->NewGlobalRef(env,obj);
g_env = env;
g_res = JNI_ERR;
return (jint)returnValueBool;
}
The class that calls the above register method is a static instance.
The callbacks occur about about 20 Hz. The callbacks are called from a single thread, which is why the DetachCurrentThread method is not called. The app typically runs ok for a few minutes, until it crashes.
The crash produces the following stack trace:
* thread #65, name = 'JVMTI Agent thr', stop reason = signal SIGSEGV: invalid address (fault address: 0x765630)
* frame #0: 0x92b8d44e libopenjdkjvmti.so`openjdkjvmti::MethodUtil::GetMethodName(_jvmtiEnv*, _jmethodID*, char**, char**, char**) + 410
Before the crash occurs, memory and cpu usage are not excessive, nor is the thread count too high. I do not believe there is a memory leak based on the android profiler view
shown here
Thanks a ton in advance!
Related
I have a problem with a Java (Eclipse RCP) application running on Window 64bit (The problem does not occur if the application runs with a 32bit JVM)
The problem also does not occur if the application is running in the debugger.
In the application debug output is made with System.err.print(...). If the application then does not run under debugger control, then the UI thread just randomly freezes.
I attached to the process with GDB and saw that the process hangs in the call of ZwWriteFile.
(gdb) where
#0 0x000000007745bdba in ntdll!ZwWriteFile () from /cygdrive/c/windows/SYSTEM32/ntdll.dll
#1 0x000007fefd3a1b3b in WriteFile () from /cygdrive/c/windows/system32/KERNELBASE.dll
#2 0x0000000077311f66 in WriteFile () from /cygdrive/c/windows/system32/kernel32.dll
#3 0x000000006651cb65 in java!handleRead () from /cygdrive/d/Programme/Razorcat/Shared/1.3/JRE_1.8/bin/java.dll
#4 0x000000006651c31e in java!JNI_OnLoad () from /cygdrive/d/Programme/Razorcat/Shared/1.3/JRE_1.8/bin/java.dll
#5 0x0000000066512dc9 in java!Java_java_io_FileOutputStream_writeBytes () from /cygdrive/d/Programme/Razorcat/Shared/1.3/JRE_1.8/bin/java.dll
(gdb) disass
Dump of assembler code for function ntdll!ZwWriteFile:
0x000000007745bdb0 <+0>: mov %rcx,%r10
0x000000007745bdb3 <+3>: mov $0x5,%eax
0x000000007745bdb8 <+8>: syscall
=> 0x000000007745bdba <+10>: retq
0x000000007745bdbb <+11>: nopl 0x0(%rax,%rax,1)
Has anyone ever had such a problem? Is there a way (kernel debugger?) to investigate this problem more closely.
Below is the function (io_util_md.c) that calls the WriteFile Windows API function. Could be that Windows has a problem with overlapped IO at pseudo handles. (I guess the process has no standard handles when not running in the debugger)
static jint writeInternal(FD fd, const void *buf, jint len, jboolean append)
{
BOOL result = 0;
DWORD written = 0;
HANDLE h = (HANDLE)fd;
if (h != INVALID_HANDLE_VALUE) {
OVERLAPPED ov;
LPOVERLAPPED lpOv;
if (append == JNI_TRUE) {
ov.offset = (DWORD)0xFFFFFFFF;
ov.OffsetHigh = (DWORD)0xFFFFFFFF;
ov.hEvent = NULL;
lpOv = &ov;
} else {
lpOv = NULL;
}
result = WriteFile(h, /* File handle to write */
buf, /* pointers to the buffers */
len, /* number of bytes to write */
&written, /* receives number of bytes written */
lpOv); /* overlapped struct */
}
if ((h == INVALID_HANDLE_VALUE) ||| (result == 0)) {
return -1;
}
return (jint)written;
}
I'm trying to get access to pdf files in my app using https://github.com/barteksc/PdfiumAndroid
and adding some functions like searching and highlighting using java and c++. the problem is that I always get a crash with no exception details. In debugging I found SIGABRT (signal SIGABRT) SIG:9 fault address
debug info
My native function is
#include <fpdftext.h>
JNIEXPORT jstring JNICALL Java_[my package]_PdfiumCore_nativeGetPageText(JNIEnv *env, jobject instance, jlong pagePtr) {
unsigned short* buffer;
FPDF_TEXTPAGE text_page = FPDFText_LoadPage((FPDF_PAGE) pagePtr);
int len = FPDFText_CountChars(text_page);
FPDFText_GetText(text_page,0,len,buffer);
jstring txt = env->NewString(buffer, len);
FPDFText_ClosePage(text_page);
return txt; }
java function in PdfiumCore.java
public String getPageText(PdfDocument doc, int pageIndex)
{
String txt;
synchronized (lock) {
try {
txt = nativeGetPageText(openPage(doc,pageIndex));
return txt;
}catch (Exception e)
{
Log.e("Error ",e.getMessage());
return "";
}
}
}
public long openPage(PdfDocument doc, int pageIndex) {
long pagePtr;
synchronized (lock) {
pagePtr = nativeLoadPage(doc.mNativeDocPtr, pageIndex);
doc.mNativePagesPtr.put(pageIndex, pagePtr);
return pagePtr;
}
}
function call
Log.e("Page 0",pdfiumCore.getPageText(pdfDocument,0));
The function retrieves the data once then the app crashes. I guess the problem is when the garbage collector tries to delete the buffer pointer which is used in FPDFText_GetText function. but I don't know how to solve it.
any suggestion please.
I solved the problem by deleting the pointer after getting the data by using this code in the JNI function
JNIEXPORT jstring JNICALL Java_[my package]_PdfiumCore_nativeGetPageText(JNIEnv *env, jobject instance,
jlong pagePtr) {
jstring txt;
FPDF_TEXTPAGE text_page = FPDFText_LoadPage((FPDF_PAGE) pagePtr);
int len = FPDFText_CountChars(text_page);
unsigned short *buffer = new unsigned short[len];
FPDFText_GetText(text_page, 0, len, buffer);
txt = env->NewString(buffer,len);
delete[](buffer);
FPDFText_ClosePage(text_page);
return txt;}
I need to call some methods from a jar using JNI in c++.But i need to call it a lot of times in different instances of thesame class.
My JNI is something like this
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
jmethodID Sim_constr = NULL;
jmethodID Read_XML = NULL;
jmethodID configure = NULL;
jmethodID initial = NULL;
jmethodID results = NULL;
jint step = 60;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=<My jar>";
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
/* load and initialize a Java VM, return a JNI interface
* pointer in env */`
long status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (status == JNI_ERR){
exit(10);
}
//Some Java method Calls... `
I need to set once the JNI and then just call 3-4 methods OR to kill/delete completely the JNI parameters and then create it again.
I suppose you may try to accomplish the following
After creating a JVM using CreateJavaVM method your JVM remains Global for all instance of the class, the environment variable(env) should be local for each instance of the class. And also env var for each instance must be used to call your methods many times using different instances.
This snippet will get you the env from JVM:(place this such that every instance has local copy of env and global copy of jvm)
After creating the instance you have to attach these env to the current thread(i.e current instance)
JNIEnv * g_env;
int getEnvStat = jvm->GetEnv((void **)&g_env, JNI_VERSION_1_8);
if (getEnvStat == JNI_EDETACHED) {
printf("GetEnv: not attached");
getEnvStat = g_vm->AttachCurrentThread((void **) &g_env, NULL);
if ( getEnvStat != 0) {
printf("Failed to attach");
}
} else if (getEnvStat == JNI_OK) {
} else if (getEnvStat == JNI_EVERSION) {
printf("GetEnv: version not supported");
}
//use the g_env variable for java method calls
//Rememeber to use this code as a local copy for each instance
now I have a question to use "JNI_GetCreatedJavaVMs".
but I couldn't use JNI_OnLoad method because my native code is not provid for java .
void *pHandle = dlopen("/system/lib/libart.so", RTLD_NOW | RTLD_GLOBAL);
JavaVM* m_pJvm = NULL;
void * pFunAddr =dlsym(pHandle, "JNI_GetCreatedJavaVMs");
LOGD("pJNI_GetCreatedJavaVMs = %08X", pFunAddr);
pJNIGetCreatedJavaVMs = (int)pFunAddr - 1;
LOGD("call !!!!!!!");
pJNIGetCreatedJavaVMs(&m_pJvm, 0, &vm_count);
LOGD("pJNIGetCreatedJavaVMs result is %d", result);
when I call the JNI_GetCreatedJavaVMs, process was crashed.
I didn't found what happend in IDA.
who can help me !!!!!!!!!!!!!! THX
ps:JNI_GetCreatedJavaVMs method is found in the android source code.
and another method is use runtime(libart.so) or gdvm(libdvm.so).
Some code from my native app, it is build in the AOSP source tree.
I am not sure if it could work in NDK
#include <jni.h>
#include <android_runtime/AndroidRuntime.h>
...
JNIEnv *env;
jint res;
JavaVM *jvm = AndroidRuntime::getJavaVM();
assert(jvm != NULL);
res = jvm->AttachCurrentThread(&env, NULL);
assert(res >= 0);
Can anybody explain to me why can I get a callback when jvm allocates some java objects, but not others? Here is what I am doing:
static jvmtiCapabilities capa;
static jvmtiEnv* jvmti = NULL;
static const char* fileName = "C:\\temp\\ObjectInitCallbackDump.txt";
static ofstream outFileStream;
void JNICALL callbackObjectAllocation ( jvmtiEnv* jvmti_env,
JNIEnv* jni_env,
jthread thread,
jobject object,
jclass object_klass,
jlong size )
{
char* generic_ptr_class;
char* class_name;
jvmtiError error;
error = jvmti_env->GetClassSignature(object_klass, &class_name, &generic_ptr_class);
if (check_jvmti_error(jvmti_env, error, "Failed to get class signature")) {
return;
}
outFileStream << class_name << std::endl;
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
jint result;
jvmtiError error;
jvmtiEventCallbacks callbacks;
outFileStream.open(fileName,ios::trunc);
result = jvm->GetEnv((void**) &jvmti, JVMTI_VERSION_1_1);
if (result != JNI_OK || jvmti == NULL) {
printf("error\n");
return JNI_ERR;
} else {
printf("loaded agent\n");
}
(void) memset(&capa, 0, sizeof(jvmtiCapabilities));
capa.can_generate_vm_object_alloc_events = 1;
error = jvmti->AddCapabilities(&capa);
if (check_jvmti_error(jvmti, error, "Unable to set capabilities") != JNI_OK) {
return JNI_ERR;
}
(void) memset(&callbacks, 0, sizeof(callbacks));
callbacks.VMObjectAlloc = &callbackObjectAllocation;
error = jvmti->SetEventCallbacks(&callbacks, (jint) sizeof(callbacks));
if (check_jvmti_error(jvmti, error, "Unable to set callbacks") != JNI_OK) {
return JNI_ERR;
}
error = jvmti->SetEventNotificationMode( JVMTI_ENABLE,
JVMTI_EVENT_VM_OBJECT_ALLOC,
(jthread) NULL);
if (check_jvmti_error(jvmti, error,
"Unable to set method entry notifications") != JNI_OK) {
return JNI_ERR;
}
return JNI_OK;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {
outFileStream.close();
}
When I examine the file that I create, I do not see the classes that I am interested in, although I know they are there and NetBeans tells me there is exactly one instance of that class in the jvm. Any thoughts???
Nikita
For performance reasons, the JVMTI only supports allocation events for objects that cannot be detected through bytecode instrumentation (BCI), as explained in the JVMTI VMObjectAlloc event documentation. This means that the event will not be triggered for most object allocations. I assume that the allocations you are missing fall within that category.
Fortunately, it's not really too difficult to intercept all object allocations using BCI. The HeapTracker demo illustrates precisely how to intercept all object allocations in a JVMTI agent using java_crw_demo in addition to the VMObjectAlloc events.
Maybe you are just not hitting your check?
If this is the code you actually run then you have a bug; you missed the ; at the end of the object
Your compare should be like this:
if (strcmp(class_name,"Ljavax/swing/JFrame;") == 0) {
printf("Got the sucker!!!");
}