1

When using JNI within SystemC I face a very strange issue I can't explain myself.

Just some information on my used environment: I am currently developing on a 12.04 ubuntu with openjdk-6 and openjdk-7 installed. The issue reproduces with both jdks!

Having this main method:

#include "tlm.h"
#include "SystemCWrapper.h"

int sc_main(int argc, char *argv[]) {
    SystemCWrapper wrapper("test");

    std::cout << "############  Before  ############" << std::endl;
    wrapper.run();

    std::cout << "############ SC-START ############" << std::endl;
    sc_core::sc_start();

    std::cout << "############   After  ############" << std::endl;
    wrapper.run();
    return 0;
}

and the following implementation of the SystemCWrapper:

#include "SystemCWrapper.h"

#include <jni.h>
#include <iostream>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

SC_HAS_PROCESS( SystemCWrapper );

using std::cout;
using std::cerr;
using std::endl;

static JavaVM * createJavaVM () {
    JavaVM *jvm;
    JNIEnv *env;
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */

    JavaVMOption* options = new JavaVMOption[2];
    options[0].optionString = const_cast<char*>("-Djava.class.path=HelloWorld.jar");
    options[1].optionString = const_cast<char*>("-verbose:jni");
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1; // NOTE: set to 2 for more verbose jni information
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface pointer in env */
    JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);

    return jvm;
}


SystemCWrapper::SystemCWrapper(sc_core::sc_module_name nam) : sc_module(nam), jvm(NULL){
    SC_THREAD(run);       // Thread to run the ISS

    // setup the JNI
    jvm = createJavaVM();
}

SystemCWrapper::~SystemCWrapper() {
}

void SystemCWrapper::run() {
    // Print general information
    cout << "Hello SystemC (PID: " << getpid() << ", PTID: " << pthread_self() << ", LTID: " << syscall(SYS_gettid) << ")" << endl;

    JNIEnv* env;

    int ret = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);

    if(ret == JNI_OK) {
        cout << "Retrieve successful." << endl;
    } else if(ret == JNI_EDETACHED){
        cerr << "Environment is detached." << endl;
    } else {
        cerr << "Retrieving environment failed! (" << ret << ")" << endl;
    }

    jclass clazz = env->FindClass("JavaProgram");
    if(clazz == NULL) {
        cerr << "Class not found" << endl;
        return;
    }
    jmethodID mConstr = env->GetMethodID(clazz, "<init>", "()V");
    if(mConstr == NULL) {
        cerr << "Constructor not found" << endl;
        return;
    }
    jmethodID mRun = env->GetMethodID(clazz, "run", "()I");
    if(mRun == NULL) {
        cerr << "Run method not found" << endl;
        return;
    }
    jobject obj = env->NewObject(clazz, mConstr);
    if(obj == NULL) {
        cerr << "Object was not created!" << endl;
        return;
    }
    int result = env->CallIntMethod(obj, mRun);
    if(result == 0) {
        cerr << "Run should never return 0!" << endl;
        return;
    }
}

Results into the following console output (Java program only outputs "Hello Java!", if invoked):

$ ./build/bin/jnitest 

    SystemC 2.3.1-Accellera --- Jun  6 2014 13:36:39
    Copyright (c) 1996-2014 by all Contributors,
    ALL RIGHTS RESERVED
############  Before  ############
Hello SystemC (PID: 5660, PTID: 140661929555776, LTID: 5660)
Retrieve successful.
Hello Java!
############ SC-START ############
Hello SystemC (PID: 5660, PTID: 140661929555776, LTID: 5660)
Retrieve successful.
Object was not created!
############   After  ############
Hello SystemC (PID: 5660, PTID: 140661929555776, LTID: 5660)
Retrieve successful.
Hello Java!

It seems that the SystemC method sc_core::sc_start(); does something that each JNI invocation is not "passed through" to Java.

I've tried some tweaks but even if I attach the current thread to the jvm nothing changes (as one can see the thread ids even remain the same).

Any suggestions? Please let me know If more information is required.

pproksch
  • 206
  • 3
  • 9
  • What are you trying to do really? Linking SystemC and a java virtual machine seams a rather strange thing to be trying to do. – Ifor Jun 15 '14 at 17:19
  • I agree that in the first sight this looks strange, but in our current situation we have our instruction set simulator implemented in Java. Now we want to integrate it into a platform simulation framework that is based on SystemC/TLM. Basically, what we do want to achieve is the same as in http://www.embecosm.com/appnotes/ean1/ean1-tlm2-or1ksim-2.0.html, but instead of building a SystemC/TLM Wrapper for a C-based simulator we want to wrap our java simulator using JNI. – pproksch Jun 16 '14 at 07:15
  • 1
    Stack size within the SC_THREAD context? Your running something far more heavyweight than is standard. – Ifor Jun 16 '14 at 15:38
  • I've tried to increase the stack size using `set_stack_size(size_t size)` but it didn't help. I also adjusted the code to call a static method only (instead of creating an object and calling a method on it) but this didn't help either. But I noticed that there is an exception thrown in the java code so i changed the code to ` public class JavaProgram { public static int run() { return -1; } } ` but the exception is still thrown. Printing the exception after the sc start() with env->ExceptionDescribe() I get an StackOverflow exception at return -1. Maybe it is related to sc_thread model? – pproksch Jun 18 '14 at 06:15

1 Answers1

0

After investigating and a big trial and error phase I came to the conclusion that this is not very simple to solve by my own. For that reason I spawned a pthread that handles the JNI invokations outside the SC_THREAD.

I assume that the issue is the QuickThreads library underneath systemc can't be used in combination with JNI. I've not found anything in this area, but I may have not searched very well.

I also could have recompiled systemc using pthreads instead of QuickThreads, but as my solution should work in both configurations this ain't an option for me.

Finally here is the code snipped that helped me out of my misery.

static JNIEnv* getJniEnv(JavaVM* jvm) {
    JNIEnv* env = NULL;
    int i = jvm->GetEnv((void**) &env, JNI_VERSION_1_6);
    if (i == JNI_EDETACHED) {
        int ret = jvm->AttachCurrentThreadAsDaemon((void **) &env, NULL);
        if ( ret != 0) {
            cerr << "Failed to attach thread to jni (" << ret << ")!\n";
        }
    } else if(i != JNI_OK) {
        cerr << "Failed retrieving jni environment!\n";
    }
    if(env == NULL) {
        cerr << "Pointer to JNIEnv is NULL!\n";
    }
    return env;
}

SystemCWrapper::SystemCWrapper(sc_core::sc_module_name nam) : sc_module(nam), jvm(NULL) {
    SC_THREAD(spawnAndRun); // Thread to run the ISS
    // setup the JNI
    jvm = createJavaVM();
}

SystemCWrapper::~SystemCWrapper() {}

static void * doStart(void * data) {
    SystemCWrapper* wrapper = (SystemCWrapper*)data;
    wrapper->run();
    return NULL;
}

void SystemCWrapper::spawnAndRun() {
    pthread_t t;
    void * ret;
    pthread_create(&t, NULL, doStart, (void*)this);
    pthread_join(t, &ret);
}

void SystemCWrapper::run() {
    // Print general information
    cout << "Hello SystemC (PID: " << getpid() << ", PTID: " << pthread_self() << ", LTID: " << syscall(SYS_gettid) << ")" << endl;
    JNIEnv* env = getJniEnv(jvm);

    if(env == NULL)
        return;

    jclass clazz = env->FindClass("JavaProgram");
    // ... same code as above ... //

}

@Ifor thanks a lot for your feedback!!!

pproksch
  • 206
  • 3
  • 9