From 397aad8d4a33bf2903191ca5348480229c410ac4 Mon Sep 17 00:00:00 2001
From: Gary Frost <frost.gary@gmail.com>
Date: Tue, 15 Jan 2013 22:26:00 +0000
Subject: [PATCH] Allow aparapi to load dll as an agent (linux 64 only at
present)
---
com.amd.aparapi.jni/build.xml | 1 +
com.amd.aparapi.jni/src/cpp/agent.cpp | 166 ++++++++++++++++++
.../src/java/com/amd/aparapi/ClassModel.java | 6 +
.../src/java/com/amd/aparapi/Config.java | 8 +
.../java/com/amd/aparapi/KernelRunner.java | 7 +-
.../src/java/com/amd/aparapi/OpenCLJNI.java | 51 +++---
examples/nbody/nbody-agent.sh | 11 ++
samples/life/life-agent.sh | 6 +
samples/mandel/mandel-agent.sh | 7 +
9 files changed, 240 insertions(+), 23 deletions(-)
create mode 100644 com.amd.aparapi.jni/src/cpp/agent.cpp
create mode 100644 examples/nbody/nbody-agent.sh
create mode 100644 samples/life/life-agent.sh
create mode 100644 samples/mandel/mandel-agent.sh
diff --git a/com.amd.aparapi.jni/build.xml b/com.amd.aparapi.jni/build.xml
index f720e85d..5d70e63e 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 00000000..e13a78d8
--- /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 e75266b6..fed2eb13 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 dad7ffb4..f45c3f8e 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 8091cf52..b0cc46df 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 1ba814c5..fbb6acd7 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 00000000..6ccb51f4
--- /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 00000000..55b917ee
--- /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 00000000..3c82e7de
--- /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
--
GitLab