Java API on top of JVMTI? - java

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.

Related

Handling JNI_CreateJavaVM ACCESS_VIOLATION in windows with _set_se_translator doesn't work

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?

JVMTI class not prepared

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.

Can we use ".so" library compiled for Linux into android?

I am writing a wrapper class for C++ ".so". I want to use the library in Java application and Android app using JNI. So I have to create header file and cpp file which will do JNI calls.
I could use that on Linux in Java application.
The steps I followed:
Created java class and called native functions in that class
public class TestWrapper {
static {
System.load("/home/native.so");
}
public static void main(String[] args) {
new TestWrapper().TestWrapper();
}
private native void sayHello();
}
Created header file and cpp file. CCP contains following code
JNIEXPORT void JNICALL Java_TestWrapper_sayHello(JNIEnv *, jobject){
uint16_t data = 0;
void (*func_print_name)(const uint16_t*);
void* handle = dlopen("libCppTobeUsed.so.0", RTLD_LAZY);
if (handle){
*(void**)(&func_print_name) = dlsym(handle, function_name);
func_print_name(&data);
dlclose(handle);
std::cout << "data received .." << data << std::endl;
}
}
}
Compiled this cpp class and generated "native.so"
This is working fine. The "native.so" could call the fuction form "ibCppTobeUsed.so.0" when called from TestWrapper.java.
I want to use same library for android as well. So, I have to write wrapper class all over again in Android NDK? Or I can compile my "native.so" for Android platform?
If I try to use it directly, I get error
"install_failed_no_matching_abis".
No, you cannot use the same shared library. Android is not GNU. You need to compile your libraries for Android.
So, I have to write wrapper class all over again in Android NDK?
No, you can write it in a way that works for both. You need to factor our your JNI wrapper class from your main class, since Android uses Activity instead of main.
I would also strongly recommend against ever relying on dlclose on any platform. The API is not sound, and will lead to surprising behavior with modern C++. A single global thread_local with a non-trivial destructor renders the library un-unloadable, so the next dlopen will not reset library state as you might expect. If you need to implement initialization/finalization logic for your library, make explicit Initialize and Finalize functions a part of the libary and call them directly.
Without knowing your architecture's full architecture I can't be sure, but from the sample you've given here I'd recommend dropping the dlopen/dlsym from your JNI entirely and just link against libCppTobeUsed directly.

How does Android's MediaPlayer work by checking the native code?

Checking the source code for MediaPlayer (link), the start() method looks like this:
public void start() throws IllegalStateException {
stayAwake(true);
_start();
}
And _start() method looks like this:
private native void _start() throws IllegalStateException;
Checking the native _start() method (link) (called start() but should it not be called _start() because of the native call was named _start()?):
status_t MediaPlayer::start()
{
ALOGV("start");
Mutex::Autolock _l(mLock);
if (mCurrentState & MEDIA_PLAYER_STARTED)
return NO_ERROR;
if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED |
MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {
mPlayer->setLooping(mLoop);
mPlayer->setVolume(mLeftVolume, mRightVolume);
mPlayer->setAuxEffectSendLevel(mSendLevel);
mCurrentState = MEDIA_PLAYER_STARTED;
status_t ret = mPlayer->start();
if (ret != NO_ERROR) {
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
} else {
if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) {
ALOGV("playback completed immediately following start()");
}
}
return ret;
}
ALOGE("start called in state %d", mCurrentState);
return INVALID_OPERATION;
}
Where is the data read? I wanted to check how Android work with RTSP but I could not find out where it loads the data. I wanted to know if it uses some library like FFMpeg or some other implementation.
EDIT:
Why was this code necessary to use JNI for?
All of the following paths reference src/frameworks/base/.
In media/jni/android_media_MediaPlayer.cpp you will find the JNI code that forwards the Java method call to the underlying native framework. You can see the name mapping in the gMethods[] array and the AndroidRuntime::registerNativeMethods call near the bottom of the file. You can read more about registering native methods with JNI here, but that's not really the interesting part.
At this stage we are in the native counterpart of the Java MediaPlayer. For the most part, it doesn't do anything interesting, either. It binds to the MediaPlayerService through IBinder transactions. The MediaPlayerService creates the actual native player based on the type of media, and maintains a client (MediaPlayerService::Client) to facilitate communication with the native MediaPlayer, which in turn bubbles things back up to Java. You can see all this happen in the following files (if you're interested):
media/libmedia/mediaplayer.cpp,
media/libmedia/IMediaPlayer.cpp,
media/libmedia/IMediaPlayerClient.cpp,
media/libmedia/IMediaPlayerService.cpp,
media/libmediaplayerservice/MediaPlayerService.cpp
Header files for libmedia are in include/media/libmedia/.
Now to the really interesting part, which are the component players. In MediaServicePlayer.cpp there are a couple of getPlayerType methods that decide what player to instantiate. There's Stagefright framework (AwesomePlayer and NuPlayer) and Sonivox player for MIDI. For RTSP, NuPlayer is what you'll get. You can find a glue layer in media/libmediaplayerservice/nuplayer/ and all the real source code in media/libstagefright/ and media/libstagefright/rtsp/.
You missed a step along the way. The private native void _start() in the Java code refers to this entry in the MediaPlayer JNI layer (which refers to this function).
As for the RTSP implementation, you'll probably find it among the Stagefright sources.
_start method is defined in jni in this mediaplayer file link
from here that libmedia start method is getting called.

Simplicity for executing Java from C++

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.

Categories