diff --git a/com.amd.aparapi.jni/build.xml b/com.amd.aparapi.jni/build.xml index f720e85d16066d21630cee991501b676252602bc..5d70e63e88e8f39fa873f7042079e117ec086370 100644 --- a/com.amd.aparapi.jni/build.xml +++ b/com.amd.aparapi.jni/build.xml @@ -517,6 +517,7 @@ First consider editing the properties in build.properties <arg value="src/cpp/opencljni.cpp" /> <arg value="src/cpp/jniHelper.cpp" /> <arg value="src/cpp/clHelper.cpp" /> + <arg value="src/cpp/agent.cpp" /> <arg value="-L${amd.app.sdk.dir}/lib/${x86_or_x86_64}" /> <arg value="-lOpenCL" /> </exec> diff --git a/com.amd.aparapi.jni/src/cpp/agent.cpp b/com.amd.aparapi.jni/src/cpp/agent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e13a78d8acdafb35b6a6f46784d3044362bab9fa --- /dev/null +++ b/com.amd.aparapi.jni/src/cpp/agent.cpp @@ -0,0 +1,166 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <jni.h> +#include <jvmti.h> + + + + +jvmtiEnv *jvmti; +JavaVM *jvm; + +static void JNICALL vmInit(jvmtiEnv *_jvmtiEnv, JNIEnv* _jniEnv, jthread thread) { + fprintf(stdout, "from agent vmInit()\n"); + /* + if (_jniEnv->ExceptionOccurred()) { + fprintf(stdout, "Exception raised\n"); + _jniEnv->ExceptionDescribe(); + _jniEnv->ExceptionClear(); + } + jclass classId = _jniEnv->FindClass("C"); + classId = _jniEnv->FindClass("java/lang/String"); + if (_jniEnv->ExceptionOccurred()) { + fprintf(stdout, "Exception raised\n"); + _jniEnv->ExceptionDescribe(); + _jniEnv->ExceptionClear(); + } + */ +} + +class NameToBytes{ + private: + const char *name; + const jint len; + const unsigned char *bytes; + const NameToBytes* next; + public: + NameToBytes(const NameToBytes *_head, const char *_name, const jint _len, const unsigned char *_bytes) : len(_len) { + int nameLen = strlen(_name)+1; + name = new char [nameLen]; + memcpy((void*)name, (void*)_name, nameLen); + for (char *ptr = (char *)name; *ptr; ptr++){ + if (*ptr == '/'){ + *ptr = '.'; + } + } + + // name = (const char*)strdup(_name); + bytes = new unsigned char[len]; + memcpy((void*)bytes, (void*)_bytes, len); + next = _head; + } + ~NameToBytes(){ + delete name; + delete bytes; + } + const unsigned char *getBytes(){ + return(bytes); + } + const char *getName(){ + return(name); + } + const jint getLen(){ + return(len); + } + const NameToBytes *getNext(){ + return(next); + } +}; + +NameToBytes *head = NULL; + +/* + * Class: com_amd_aparapi_OpenCLJNI + * Method: getClassBytes + * Signature: (Ljava/lang/String;)V + */ +#ifdef __cplusplus +extern "C" { +#endif +JNIEXPORT jbyteArray JNICALL Java_com_amd_aparapi_OpenCLJNI_getBytes (JNIEnv *jenv, jobject instance, jstring className){ + jbyteArray bytes = NULL; + const char *nameChars = jenv->GetStringUTFChars(className, NULL); + fprintf(stdout, "inside getBytes(\"%s\")\n", nameChars); + for (NameToBytes *ptr = head; ptr != NULL; ptr=(NameToBytes *)ptr->getNext()){ + //fprintf(stdout, "testing \"%s\"==\"%s\"\n", nameChars, ptr->getName()); + if (!strcmp(ptr->getName(), nameChars)){ + fprintf(stdout, "found bytes for \"%s\"\n", nameChars); + bytes = jenv->NewByteArray(ptr->getLen()); + //fprintf(stdout, "created byte array size= %d\n", ptr->getLen()); + jenv->SetByteArrayRegion(bytes, (jint)0, (jint)ptr->getLen() , (jbyte*)ptr->getBytes()); + break; + } + } + if (bytes == NULL){ + fprintf(stdout, "failed to find bytes for \"%s\"\n", nameChars); + } + jenv->ReleaseStringUTFChars(className, nameChars); + return (bytes); +} +#ifdef __cplusplus +} +#endif + + +static void JNICALL cbClassFileLoadHook(jvmtiEnv *jvmti_env, JNIEnv* jni_env, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data){ +// fprintf(stdout, "from agent classFileLoadHook(%s)\n", name); + head = new NameToBytes(head, name, class_data_len, class_data); +} + + +JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { + jint returnValue = 0; // OK + jint rc; + jvmtiError err; + jvmtiCapabilities capabilities; + jvmtiEventCallbacks callbacks; + fprintf(stdout, "Agent_Onload()\n"); + + // Get a handle on the JVM. + jvm = vm; + + /* Get JVMTI environment */ + rc = vm->GetEnv((void **)&jvmti, JVMTI_VERSION); + if (rc != JNI_OK) { + fprintf(stderr, "ERROR: Unable to create jvmtiEnv, GetEnv failed, error=%d\n", rc); + returnValue = -1; + }else{ + /* Get/Add JVMTI capabilities */ + if ((err = jvmti->GetCapabilities(&capabilities) ) != JVMTI_ERROR_NONE) { + fprintf(stderr, "ERROR: GetCapabilities failed, error=%d\n", err); + returnValue = -1; + }else{ + capabilities.can_tag_objects = 1; + capabilities.can_generate_all_class_hook_events = 1; + if ((err = jvmti->AddCapabilities(&capabilities))!= JVMTI_ERROR_NONE) { + fprintf(stderr, "ERROR: AddCapabilities failed, error=%d\n", err); + returnValue = -1; + }else{ + /* Set callbacks and enable event notifications */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.VMInit = &vmInit; + callbacks.ClassFileLoadHook = &cbClassFileLoadHook; + + jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks)); + jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL); + jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); + } + } + } + return returnValue; +} + +/* Agent_OnUnload() is called last */ +JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) { + fprintf(stdout, "Agent_OnUnload()\n"); +} + diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/ClassModel.java b/com.amd.aparapi/src/java/com/amd/aparapi/ClassModel.java index e75266b6e04198ae31a0d4b1602856a29daf6021..fed2eb1378bec2cb593927a21e17948d2e4abe2e 100644 --- a/com.amd.aparapi/src/java/com/amd/aparapi/ClassModel.java +++ b/com.amd.aparapi/src/java/com/amd/aparapi/ClassModel.java @@ -38,6 +38,7 @@ under those regulations, please refer to the U.S. Bureau of Industry and Securit package com.amd.aparapi; import java.io.InputStream; +import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -152,6 +153,11 @@ class ClassModel{ } + ClassModel(Class<?> _clazz, byte[] _bytes) throws ClassParseException { + clazz = _clazz; + parse(new ByteArrayInputStream(_bytes)); + } + /** * Determine if this is the superclass of some other named class. * diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/Config.java b/com.amd.aparapi/src/java/com/amd/aparapi/Config.java index dad7ffb41ae8c7174cbfd412d26c260230bc4195..f45c3f8e6f7e7f814ee0aa3e52d7e3e6f2658ca8 100644 --- a/com.amd.aparapi/src/java/com/amd/aparapi/Config.java +++ b/com.amd.aparapi/src/java/com/amd/aparapi/Config.java @@ -56,6 +56,14 @@ class Config{ private static final String propPkgName = Config.class.getPackage().getName(); + /** + * Allows the user to request to use a jvmti agent to access JNI code rather than loading explicitly. + * + * Usage -agentpath=/full/path/to/agent.dll -Dcom.amd.aparapi.useAgent=true + */ + + static final boolean useAgent = Boolean.getBoolean(propPkgName + ".useAgent"); + static final boolean disableUnsafe = Boolean.getBoolean(propPkgName + ".disableUnsafe"); /** diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/KernelRunner.java b/com.amd.aparapi/src/java/com/amd/aparapi/KernelRunner.java index 8091cf52a399be44bcf0005353196d723903fc02..b0cc46dfc69b639cca90c67ec80bdd9626b1dc37 100644 --- a/com.amd.aparapi/src/java/com/amd/aparapi/KernelRunner.java +++ b/com.amd.aparapi/src/java/com/amd/aparapi/KernelRunner.java @@ -1371,7 +1371,12 @@ class KernelRunner{ if ((device == null) || (device instanceof OpenCLDevice)) { if (entryPoint == null) { try { - ClassModel classModel = new ClassModel(kernel.getClass()); + ClassModel classModel = null; + if (Config.useAgent){ + classModel = new ClassModel(kernel.getClass(), OpenCLJNI.getJNI().getBytes(kernel.getClass().getName())); + }else{ + classModel = new ClassModel(kernel.getClass()); + } entryPoint = classModel.getEntrypoint(_entrypointName, kernel); } catch (Exception exception) { return warnFallBackAndExecute(_entrypointName, _range, _passes, exception); diff --git a/com.amd.aparapi/src/java/com/amd/aparapi/OpenCLJNI.java b/com.amd.aparapi/src/java/com/amd/aparapi/OpenCLJNI.java index 1ba814c5d0caeac849994ceeeca808144685b558..fbb6acd7b0b38a9cc94211e1b75a27430f52dc05 100644 --- a/com.amd.aparapi/src/java/com/amd/aparapi/OpenCLJNI.java +++ b/com.amd.aparapi/src/java/com/amd/aparapi/OpenCLJNI.java @@ -8,29 +8,34 @@ public class OpenCLJNI{ static boolean openCLAvailable = false; static { - String arch = System.getProperty("os.arch"); - logger.fine("arch = " + arch); - String aparapiLibraryName = null; - - if (arch.equals("amd64") || arch.equals("x86_64")) { - aparapiLibraryName = "aparapi_x86_64"; - } else if (arch.equals("x86") || arch.equals("i386")) { - aparapiLibraryName = "aparapi_x86"; - } else { - logger.warning("Expected property os.arch to contain amd64, x86_64, x86 or i386 but instead found " + arch - + " as a result we don't know which aparapi to attempt to load."); - } - if (aparapiLibraryName != null) { - logger.fine("attempting to load aparapi shared lib " + aparapiLibraryName); - try { - Runtime.getRuntime().loadLibrary(aparapiLibraryName); - openCLAvailable = true; - } catch (UnsatisfiedLinkError e) { - System.out - .println("Check your environment. Failed to load aparapi native library " - + aparapiLibraryName - + " or possibly failed to locate opencl native library (opencl.dll/opencl.so). Ensure that both are in your PATH (windows) or in LD_LIBRARY_PATH (linux)."); + if (Config.useAgent){ + logger.fine("Using agent!"); + openCLAvailable = true; + }else{ + String arch = System.getProperty("os.arch"); + logger.fine("arch = " + arch); + String aparapiLibraryName = null; + if (arch.equals("amd64") || arch.equals("x86_64")) { + aparapiLibraryName = "aparapi_x86_64"; + } else if (arch.equals("x86") || arch.equals("i386")) { + aparapiLibraryName = "aparapi_x86"; + } else { + logger.warning("Expected property os.arch to contain amd64, x86_64, x86 or i386 but instead found " + arch + + " as a result we don't know which aparapi to attempt to load."); + } + if (aparapiLibraryName != null) { + logger.fine("attempting to load aparapi shared lib " + aparapiLibraryName); + try { + Runtime.getRuntime().loadLibrary(aparapiLibraryName); + openCLAvailable = true; + } catch (UnsatisfiedLinkError e) { + System.out + .println("Check your environment. Failed to load aparapi native library " + + aparapiLibraryName + + " or possibly failed to locate opencl native library (opencl.dll/opencl.so). Ensure that both are in your PATH (windows) or in LD_LIBRARY_PATH (linux)."); + + } } } } @@ -53,6 +58,8 @@ public class OpenCLJNI{ native public void getMem(OpenCLProgram program, OpenCLMem mem); + native public byte[] getBytes(String className); + public boolean isOpenCLAvailable() { return (openCLAvailable); } diff --git a/examples/nbody/nbody-agent.sh b/examples/nbody/nbody-agent.sh new file mode 100644 index 0000000000000000000000000000000000000000..6ccb51f4f50c58cee17c6e5e9adfb3a49fc48d56 --- /dev/null +++ b/examples/nbody/nbody-agent.sh @@ -0,0 +1,11 @@ +java \ + -agentpath:../../com.amd.aparapi.jni/dist/libaparapi_x86_64.so\ + -Dcom.amd.aparapi.useAgent=true\ + -Djava.library.path=../third-party/jogamp \ + -Dcom.amd.aparapi.executionMode=$1 \ + -Dbodies=$2 \ + -Dheight=600 \ + -Dwidth=600 \ + -classpath ../third-party/jogamp/jogl-all.jar:../third-party/jogamp/gluegen-rt.jar:../../com.amd.aparapi/dist/aparapi.jar:nbody.jar \ + com.amd.aparapi.examples.nbody.Main + diff --git a/samples/life/life-agent.sh b/samples/life/life-agent.sh new file mode 100644 index 0000000000000000000000000000000000000000..55b917eed8f32798dbfe3682a58ec09d69763314 --- /dev/null +++ b/samples/life/life-agent.sh @@ -0,0 +1,6 @@ +java\ + -agentpath:../../com.amd.aparapi.jni/dist/libaparapi_x86_64.so\ + -Dcom.amd.aparapi.executionMode=$1\ + -Dcom.amd.aparapi.useAgent=true\ + -classpath ../../com.amd.aparapi/dist/aparapi.jar:life.jar\ + com.amd.aparapi.sample.life.Main diff --git a/samples/mandel/mandel-agent.sh b/samples/mandel/mandel-agent.sh new file mode 100644 index 0000000000000000000000000000000000000000..3c82e7de596c2ced3a24ed9d2a248aa2c9bc131e --- /dev/null +++ b/samples/mandel/mandel-agent.sh @@ -0,0 +1,7 @@ +java\ + -agentpath:../../com.amd.aparapi.jni/dist/libaparapi_x86_64.so\ + -Djava.library.path=../../com.amd.aparapi.jni/dist\ + -Dcom.amd.aparapi.useAgent=true\ + -Dcom.amd.aparapi.executionMode=$1\ + -classpath ../../com.amd.aparapi/dist/aparapi.jar:mandel.jar\ + com.amd.aparapi.sample.mandel.Main