Debugging a c project that is attempting to call some java functions in a jar by using JNI_CreateJavaVM(), GetStaticMethod(), CallStaticVoidMethod() etc.
Using -> openJdk 1.6.045.
I wrote a small test program that looks similar to this:
Sample program calling Java from C
Which works.
The problem comes when I integrate this with a lot of other code. The other code crashes shortly after JNI_CreateJavaVM() - (the other code works fine otherwise). It seems that JNI_CreateJavaVM() messes with the stack.
If I set the JavaVM up like so:
JavaVMOption options[2];
// Adjust stacksize.
options.[0].optionString = "path/to/class";
options.[1].optionString = "-Xss65536k";
vm_args.nOptions = 2;
vm_args.options = options;
int ret = JNI_CreateJavaVM(&jvm, (void **) &jenv, &vm_args);
Then the C program continues on happily, otherwise, if I omit the "-Xss" option, the C program terminates when it attempts to access the stack.
Any clues as to what is really going on? I suspect stack corruption, but I'd like to be able be certain as to what is happening.
Related
I am calling OpenJDK's JNI_CreateJavaVM in windows to use the JVM. Calling the function causes ACCESS_VIOLATION (while it works fine in ubuntu).
After some research in Stack Overflow and the web, it seems it is a normal(?!) operation of the function (one finding is https://bugs.openjdk.org/browse/JDK-8181081).
I am trying to handle this bizarre behavior by handling the ACCESS_VIOLATION using _set_se_translator, but by translator function is not called and the application crashes.
My function looks like this:
jint create_jvm(JavaVM** pvm, void** penv, void* args)
{
auto prev = _set_se_translator(handleAccessViolationFromCreateJVMInWindows);
jint res = JNI_CreateJavaVM(pvm, penv, args); // <-- ACCESS_VIOLATION
_set_se_translator(prev);
return res;
}
The translator looks like this:
void handleAccessViolationFromCreateJVMInWindows(unsigned int code, struct _EXCEPTION_POINTERS* ep)
{
printf("+++++++++++++++++++++ HANDLE ACCESS VIOLATION\r\n");
}
I am also adding /EHa flag in my CMake (I am using CLion/Cmake):
if(MSVC)
target_compile_options(${target_name} PUBLIC "/EHa")
endif()
The problem is that the translator is not called and the program crashes.
I have also previously tried to use __try/__except without success.
Any ideas?
I have the following Java code generated using SWIG. Although SWIG was used to generate the code knowledge of SWIG should not be needed to understand this question
User facing Java code:
public class UuidUtil implements OCUuidUtilConstants {
public static Uuid generateUuid() {
long cPtr = UuidUtilJNI.generateUuid();
return (cPtr == 0) ? null : new Uuid(cPtr, true);
}
}
The UuidUtilJNI.generateUuid() is defined as follows
Java code calling JNI code
public class UuidUtilJNI {
public final static native long generateUuid();
}
The SWIG generated JNI C wrapper code uuid_wrap.c
uuid_t * jni_gen_uuid()
{
printf("Inside jni_gen_uuid\n");
uuid_t *value = (uuid_t *)malloc(sizeof(uuid_t));
printf("Calling gen_uuid\n");
gen_uuid(value);
return value;
}
SWIGEXPORT jlong JNICALL Java_UuidUtilJNI_generateUuid(JNIEnv *jenv, jclass jcls) {
jlong jresult = 0 ;
uuid_t *result = 0 ;
(void)jenv;
(void)jcls;
result = (uuid_t *)jni_gen_uuid();
*(uuid_t **)&jresult = result;
return jresult;
}
partial C code for gen_uuid(uuid_t *uuid) function uuid.c
void gen_uuid(uuid_t *uuid)
{
printf("Inside gen_uuid\n");
/* code to set the uuid to type 4 UUID according to RFC4122 */
}
I am testing it as follows:
#Test
public void generateAndConvert() {
Uuid testUuid = UuidUtil.generateUuid();
assertNotNull(testUuid);
// other test code left out for readability
}
When the test is run on Windows the code works as expected.
When I try and run the same code on Linux the test hangs.
It prints 2 of the 3 print staments found in the code.
Inside jni_gen_uuid
Calling gen_uuid
the line Inside gen_uuid is never called when the test is run on a Linux machine (Fedora 30).
My first thought was that there was some sort of mismatch in the output of the object files causing the wrapper code to fail when calling the uuid code. I have compared the build flags used to build uuid.c and uuid_wrap.c code and they are using the same flags with the exception that some of the build warning are shut off for the wrapper code because its generated by a tool and not intended to
be modified by me.
I don't really know where else to look.
I have checked the uuid.o file using nm and objdump as best I can tell it has the gen_uuid symbol.
I have not been able to properly attach gdb to the running sample so not sure if that would provide any useful information.
Any suggestions that may help find the issue?
Unfortunately this solution was very specific to my code.
When I wrote that the print statement
printf("Inside gen_uuid\n");
/* code to set the uuid to type 4 UUID according to RFC4122 */
was never called I was wrong. The text was just not flushed to the screen. Adding fflush(stdout); after the print statement would have shown that indeed the gen_uuid function was being called.
As noted in the question this code generates a type 4 UUID which is a random UUID.
The code that generates random numbers is OS specific. Before the random number can be read the random number generator must be initialized. On Linux this is done by grabbing a file descriptor to /dev/urandom on Windows it is done by seeding srand() function.
Linux would segfault when generating the random number if random_init was not called. Windows on the other hand would produce a number even if it were not seeded.
The solution was to call randomInit() before calling generateUuid() in the unit test.
If you are a developer exploring a similar failure and are using printf debugging to check if a line of code was reached then make sure the print statement is flushed to the screen before concluding that a line of code is not being reached.
I have a C routine that is calling a Java module through the JNI invocation interface. I've been having an issue where the call to the Java method has been returning a NULL string when using the C module and the JNI, but when I use Java at the command line, the Java module returns an error and a default value.
Here is the data returned when I bypass the C code and call the Method through Java at the command line
$ java ClassName "This" "is" "my" "test" "string"
Exception on SomethingElse.Method: [Function: DifferentMethod]ExceptionClassException: ExceptionClassExceptionException: [Function: CFBDynamicMessage::getCodePage]codePageBytes is NULL, for UTF-8
Returned String -- data I'm trying to get
0.0| |0.0| |0.0|| |0.0|0| |0.0| | ||0.0|0|0|0|0|0|0|0|0|0|0||
I need to get the returned string, even in the event of an error on the java side. To try to see what is going on in the C and JNI, I turned up the debugging level for the JNI:
options[1].optionString = "-Xdebug"; /* print JNI-related messages */
options[2].optionString = "-Xlog:all";
// this next line allows us to continue processing after the Java
// error. Unfortunately the code then SegFaults further on.
options[3].optionString = "-Xcheck:jni:nonfatal";
options[4].optionString = "-verbose:jni";
The JNI starts the JVM, finds the class and the method and builds the Java string I need to pass to the method, but when I try to execute the method, I get the following errors:
JVMJNCK048E JNI error in CallObjectMethod/CallObjectMethodV: Ineligible receiver
JVMJNCK080E Error detected in the outermost frame of an attached thread
JVMJNCK023E JNI error detected. Continuing...
Segmentation fault (core dumped)
Here is the C Code (I am cutting out some error checking and whitespace for brevity):
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
cls = (*env)->FindClass(env, "GenericClassName");
mid = (*env)->GetMethodID(env, cls, "execute", "(Ljava/lang/Object;)Ljava/lang/Object;");
jstr = (*env)->NewStringUTF( env, "This|is|my|test|string|");
// This next line is the one that throws the ineligible receiver error
jstring resultStr = (*env)->CallObjectMethod(env, cls, mid, jstr);
// ExceptionOccurred(env) is false; no errors are displayed
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
const char *nativeString = (*env)->GetStringUTFChars(env, resultStr, JNI_FALSE);
// This next line calls the SegFault
fprintf(stdout, "%s\n", *nativeString);
I've used gdb to look at the core dump created by the SegFault and here are the what I think are the pertinent results:
print nativeString
$1 = 0x8dfaa80 "[Ljava.lang.Class;#34ca34ca"
print *nativeString
$2 = 91 '['
here is the definition of the java method I'm calling:
public Object execute (Object args) throws Exception { ... }
Any help or insight you can provide with this issue will be greatly appreciated. When I try to Google for the ineligible receiver error, I get a bunch of links about football, but nothing in the JNI or Java in general. I've spent the last few days scouring the Stack Overflow website, and although there were some promising questions asked, they were all related to Android and didn't provide any additional assistance in resolving my issue.
As two final notes, even if you can't help, thanks for reading this far and my environment is a 64bit RHEL server, but the C code and the JVM are both running as 32bit applications.
TL;DR - C is calling a Java Method through the JNI and I am getting an error code (JNI error in CallObjectMethod/CallObjectMethodV: Ineligible receiver) that results in Google telling me all about all the sports I can't watch because I'm working on this issue.
You're supplying a class instead of an object. So you're trying to call Class<GenericClassName>.execute(Object arg). You need an instance of the class.
Background info: I am used to program in Java and I know how to use Eclipse and Visual Studio.
Final objective: to create a GUI, preferably in Visual Studio, which executes Java functions.
What I wish to accomplish from this question: a button in C++ which, on click, executes a Java function and returns the results to C++. (probably by invoking a JVM)
I've currently considered the following datastructures:
Sharing data through 'common' files such as .txt files (but then how do I start the Java functions?)
Opening a socket (seems too complicated for this problem)
Connecting through a server (too complicated)
Invoking a JVM from C++ which then executes the Java file (I think this is the most reasonable way but this needs a lot of code)
Now I know about the existence of Jace, JNI and SWIG but I think they are very handy for making complicated programs, not easy interfaces. I don't want to make a complicated program hence I feel that learning all their commands is quite bothersome.
I have also read up on a lot of Stack Exchange questions asking the exact same thing but all of them seem to give very complicated answers.
So here is my question:
What is the absolute simplest way to execute a (if necessary: precompiled) Java function from C++ where the C++ code passes some arguments to this Java function
Thanks in advance.
Invoking a JVM from C++ which then executes the Java file (I think this is the most reasonable way but this needs a lot of code)
Yes, it definitely is the most reasonable way. And with JNI and the invocation API it's not even that much code.
Finding the jvm.dll
You could try things like hardcoding the path to the Oracle JVM's jvm.dll or searching for a file called jvm.dll in the programs folder, but all that is obviously extremely hacky. However, there is apparently a pretty easy solution: The registry. The key HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment contains a REG_SZ called CurrentVersion. You can read the value of this key (currently it's 1.7) and open a child key with that name (HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7 in this example). That key will then contain a REG_SZ called RuntimeLib which is the path to your jvm.dll. Don't worry about Program files vs Program files (x86). WOW64 will automatically redirect your registry query to HKLM\SOFTWARE\Wow6432Node if you're a 32bit process on a 64bit windows and that key contains the path to the 32 bit jvm.dll. Code:
#include <Windows.h>
#include <jni.h> // C:\Program Files\Java\jdk1.7.0_10\include\jni.h
// ...
DWORD retval;
// fetch jvm.dll path from registry
HKEY jKey;
if (retval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\JavaSoft\\Java Runtime Environment"), 0, KEY_READ, &jKey))
{
RegCloseKey(jKey);
// assuming you're using C++/CLI
throw gcnew System::ComponentModel::Win32Exception(retval);
}
TCHAR versionString[16]; // version numbers shouldn't be longer than 16 chars
DWORD bufsize = 16 * sizeof(TCHAR);
if (retval = RegGetValue(jKey, NULL, TEXT("CurrentVersion"), RRF_RT_REG_SZ, NULL, versionString, &bufsize))
{
RegCloseKey(jKey);
// assuming you're using C++/CLI
throw gcnew System::ComponentModel::Win32Exception(retval);
}
TCHAR* dllpath = new TCHAR[512];
bufsize = 512 * sizeof(TCHAR);
retval = RegGetValue(jKey, versionString, TEXT("RuntimeLib"), RRF_RT_REG_SZ, NULL, dllpath, &bufsize)
RegCloseKey(jKey);
if (retval)
{
delete[] dllpath;
// assuming you're using C++/CLI
throw gcnew System::ComponentModel::Win32Exception(retval);
}
Loading the jvm.dll and getting the CreateJavaVM function
This part is pretty straightforward, you just use LoadLibrary and GetProcAddress:
HMODULE jniModule = LoadLibrary(dllpath);
delete[] dllpath;
if (jniModule == NULL)
throw gcnew System::ComponentModel::Win32Exception();
typedef int (JNICALL * JNI_CreateJavaVM)(JavaVM** jvm, JNIEnv** env, JavaVMInitArgs* initargs);
JNI_CreateJavaVM createJavaVM = (JNI_CreateJavaVM)GetProcAddress(jniModule, "JNI_CreateJavaVM");
Creating the JVM
Now you can invoke that function:
JavaVMInitArgs initArgs;
initArgs.version = JNI_VERSION_1_6;
initArgs.nOptions = 0;
JavaVM* jvm;
JNIEnv* env;
if ((retval = createJavaVM(&jvm, &env, &initArgs)) != JNI_OK)
throw gcnew System::Exception(); // beyond the scope of this answer
Congratulations! There's now a JVM running right inside your process! You would probably launch the JVM at the startup of your application. Unless you are 100% sure that you will only ever invoke Java code from the thread that just created the JVM, you can throw away the env pointer, but you have to keep the jvm pointer.
Getting the JNI environment (optional)
So now you created the JVM and your application is up and running and then somebody clicks that button. Now you want to invoke Java code. If you are 100% sure that you are right now on the thread that created the JVM in the previous step and you still have the env pointer, then you can skip this. Otherwise, perform a quick check if the current thread is attached to the JVM and attach it if it isn't:
JNIEnv* env;
bool mustDetach = false;
jint retval = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
if (retval == JNI_EDETACHED)
{
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6;
args.name = NULL;
args.group = NULL;
retval = jvm->AttachCurrentThread(&env, &args);
mustDetach = true; // to clean up afterwards
}
if (retval != JNI_OK)
throw gcnew System::Exception(); // should never happen
invokeJavaCode(env); // next step
if (mustDetach)
jvm->DetachCurrentThread();
Invoking Java code
Now you are right there, you want to invoke that Java code and you even have the env pointer. You want the easiest solution, so this is how you call a static method:
jclass clazz = env->FindClass("com/myself/MyClass");
if (clazz == NULL)
throw gcnew System::Exception();
jmethodID mid = env->GetStaticMethodID(clazz, "myStaticMethod", "<signature>");
if (mid == NULL)
throw gcnew System::Exception();
<type> returnedValue = env->CallStatic<type>Method(clazz, mid, <arguments>);
You can use javap -s (command line tool) to determine a method's signature. <type> can be any primitive type (it must match the return type of the Java method). The arguments can be of any primitive type, as long as they match the arguments of the Java method.
The end
And there you have it: The easiest way to invoke Java code from C++ on Windows (actually only the first two parts are windows-specific...). Oh, and also the most efficient one. Screw databases and files. Using 127.0.0.1 sockets would be an option but that's significantly less efficient and probably not less work than this. Wow, this answer is a bit longer than I expected. Hopefully it helps.
I have a Java app which needs to interact with the camera on a Windows Mobile device. I have written the Java Code and the Native code and it all works fine. The problem I am having now is that I want to start passing variables from Java to the Native code, e.g. the directory and file name to use for the photo.
The native code uses a SHCAMERACAPTURE object to interact with the camera and it expects the directory and filename to be specified using LPCTSTRs. The string passed in is a jstring, which I can get to a const char * by calling:
const char *strDir=(jEnv)->GetStringUTFChars(dirName, 0);
But I am not sure how I can pass this to the the SHCAMERACAPTURE object because it cannot convert const char * to LPCTSTR. I tried a cast (LPCTSTR)strDir and it compiled, but I get an error when it runs (that it can't create the file).
I am a Java developer and pretty new to C++ etc. so I am really not too sure what I need to do to get my string into the native call. Any ideas?
I think you should try GetStringChars() instead of GetStringUTFChars()
According to this page it returns the Unicode String.
WindowsCE and Windows mobile use UNICODE exclusively so LPCTSTR
is actually LPCWSTR (Long Pointer to Const WideChar String)
SHCAMERACAPTURE shcc;
ZeroMemory(&shcc, sizeof(shcc));
shcc.cbSize = sizeof(shcc);
shcc.pszInitialDir = (TCHAR*)(jEnv)->GetStringChars(dirName, 0 );
shcc.pszDefaultFileName = (TCHAR*)(jEnv)->GetStringChars(defFileName, 0 );
I assume you want to provide a path and a filename. This is adapted from this MS page