JNI:调用memset发生的函数导致jvm-fetch在第二次调用中崩溃

时间:2015-12-29 15:43:24

标签: c jvm java-native-interface memset

这是首先执行一些memset操作然后调用基本上创建/获取JVM然后与JVM通信的函数的函数。

int
acCreateHeader(acInputParms *acCreate, acReturnParms *acRet3){

   int     Debug;
    int     rc;
    acInputParms        acInp;
    kc0InputParms       kc0Inp;
    kc0ReturnParms      kc0Ret3;

    initACReturnParms(acRet3);

    rc = invokeKc000(kc0Inp, &kc0Ret3); // JNI Operations

    return rc;
}

initACReturnParms(acRet3);的扩展如下:

void initACReturnParms (acReturnParms *in) {

    memset(in->msgId,0,sizeof(in->msgId));
    memset(in->msgText,0,sizeof(in->msgText));
    memset(in->returnHdrTrl,0,sizeof(in->returnHdrTrl));
    memset(in->returnPaySrc,0,sizeof(in->returnPaySrc));
    memset(in->returnPayLoc,0,sizeof(in->returnPayLoc));
    return;
}

所以,当我执行整个程序时,第一个JNI操作发生得很好,但是程序的逻辑构建方式是函数acCreateHeader被调用两次,因此程序冻结在函数(**jvm)->GetEnv(*jvm, (void**)&env, JNI_VERSION_1_4);(基本上尝试在第二次调用中获取已创建的JVM)。

很抱歉,几乎不可能在此处粘贴我的所有代码,因为程序非常大。

但问题是:

当我复制函数initACReturnParms的所有逻辑并通过评论acCreateHeader的调用(如下所示)将其粘贴到函数initACReturnParms中时,程序不会崩溃即使在第二次JNI操作之后,整个程序也按照我的预期运行。

int
acCreateHeader(acInputParms *acCreate, acReturnParms *acRet3){

   int     Debug;
    int     rc;
    acInputParms        acInp;
    kc0InputParms       kc0Inp;
    kc0ReturnParms      kc0Ret3;

    memset(acRet3->msgId,0,sizeof(acRet3->msgId));
    memset(acRet3->msgText,0,sizeof(acRet3->msgText));
    memset(acRet3->returnHdrTrl,0,sizeof(acRet3->returnHdrTrl));
    memset(acRet3->returnPaySrc,0,sizeof(acRet3->returnPaySrc));
    memset(acRet3->returnPayLoc,0,sizeof(acRet3->returnPayLoc));
    //initACReturnParms(acRet3);

    rc = invokeKc000(kc0Inp, &kc0Ret3); // JNI Operations

    return rc;
}

那么,为什么这个函数initACReturnParms在这里召唤罪魁祸首?请帮我理解。

如果您希望我提供更多信息/代码,请与我们联系。

更新

acRet3是函数acCreateHeader中的局部变量,并从以下函数作为参数传递:

void writeLSHeader() {

  int rc;

  fillHdrTrl();

  rc=acCreateHeader(&acCreate,&acRet);

  //rest of the codes
}

acRet在以下显示的函数中初始化:

int fillHdrTrl() {
    //Codes here
    memset(&acRet,0,sizeof(acReturnParms));
    memset(&acRet.returnHdrTrl[0],' ',AC_HDR_TRL_SZ);
    //Codes here
}

acRet是一个全局变量,内存在堆栈上分配:

acReturnParms acRet;

acReturnParms看起来像这样:

struct acReturnParms {
    char msgId[9];
    char msgText[61];
    char returnHdrTrl[351];
    char returnPaySrc[9];
    char returnPayLoc[3];
}

上面使用的宏如下所示出现在头文件中:

#define AC_HDR_TRL_SZ            350

更新2

以下是JNI操作的代码,这可能是导致此未定义行为的原因:

#include "jni_funct.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int invokeKc000(kc0InputParms kc0inp, kc0ReturnParms *kc0ret)
{
    JavaVM *jvm;
    JNIEnv *env;
    kc0InputParms input;
    kc0ReturnParms *kc000out;
    char *kc910Path, *kc910Lib;
    int retcode;
    retcode = 8;

    kc910Path = getenv("KC910_BIN");
    kc910Lib = getenv("KC910_LIB");
    if(kc910Path == NULL || strlen(kc910Path) <= 0){
        return -1;
    }

    if(kc910Lib == NULL || strlen(kc910Lib) <= 0){
        return -1;
    }
    env = createKc000Vm(&jvm, kc910Path, kc910Lib);
    if(env == NULL){
        printf("\n JNIEnv is NULL");
        return -2;
    }
    input = kc0inp;
    kc000out = kc0ret;
    communicateKc000(env, input, &kc000out, &retcode);
    return retcode;
}

JNIEnv* createKc000Vm(JavaVM **jvm, char *kc910Path, char *kc910lib)
{
        JNIEnv* env;
        JavaVMInitArgs args;
        JavaVMOption options;
        args.version = JNI_VERSION_1_4;
        args.nOptions = 1;
        char *classPath = "shared/misc/kc000";
        char *javaClassPathOption = "-Djava.class.path";
        char javaOptionString[2000];
        char kc910Lib[1000], kc910Bin[1000];

        memset(kc910Lib, '\0', sizeof(kc910Lib));
        memset(kc910Bin, '\0', sizeof(kc910Bin));
        memset(javaOptionString, '\0', sizeof(javaOptionString));
        strncpy(javaOptionString, javaClassPathOption, strlen(javaClassPathOption));
        strncpy(kc910Lib, kc910lib, strlen(kc910lib));
        strncpy(kc910Bin, kc910Path, strlen(kc910Path));
        strcat(javaOptionString, "=");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/com.ibm.ws.ejb.thinclient_8.5.0.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/com.ibm.ws.runtime.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/com.ibm.ws.orb_8.5.0.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/ojdbc6.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/providerutil.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/j2ee.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/src.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/junit.jar:");
        strcat(javaOptionString, kc910Lib);
        strcat(javaOptionString, "/ldap.jar:");
        strcat(javaOptionString, classPath);
        strcat(javaOptionString, ":");
        strcat(javaOptionString, kc910Path);
        strcat(javaOptionString, "/C2J2.jar");

        options.optionString = javaOptionString;
        args.options = &options;
        args.ignoreUnrecognized = 0;
        int rv;
        jsize buf, num;
        rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
        if (rv < 0 || !env){
                printf("\nUnable to launch KC000 JVM %d. Perhaps you are attempting to create JVM. Attempting to fetch existing JVM, if running",rv);
                JNI_GetCreatedJavaVMs(jvm, buf, &num);
                (**jvm)->GetEnv(*jvm, (void**)&env, JNI_VERSION_1_4);
        }
        else
                printf("\nLaunched kc000 JVM!");

        return env;
}

void communicateKc000(JNIEnv* env, kc0InputParms in, kc0ReturnParms **kc000outparms, int *retcode)
{
    jclass init_class;
    jmethodID init_method;
    jstring inString, outString;
    kc0ReturnParms **kc000out;
    char returnCodeString[9];
    int flag = 8, i = 0;
    kc000out = kc000outparms;
    inString = (*env)->NewStringUTF(env, in.inStream);

    init_class = (*env)->FindClass(env, "shared/misc/kc000/Kc000");
    init_method = (*env)->GetStaticMethodID(env, init_class, "init", "(Ljava/lang/String;)Ljava/lang/String;");
    outString = (*env)->CallStaticObjectMethod(env, init_class, init_method, inString);
    const char *rawString = (*env)->GetStringUTFChars(env, outString, 0);
    jclass newExcCls = (*env)->FindClass(env, "java/lang/Throwable");
    if(newExcCls == NULL)
    {
        /*printf("Exception not found\n");*/
    }
    else
    {
        (*env)->ExceptionDescribe(env);
    }   
    strcpy((*kc000out)->inStream, rawString);
    memset(returnCodeString, '\0', sizeof(returnCodeString));
    strncpy(returnCodeString, rawString, 8);
    printf("KC000 return code: [%s]\n", returnCodeString);
    if(strlen(returnCodeString) > 0){
        for(i = 0; i < 8; i++){
            if(returnCodeString[i] == ' '){
                flag = 0;
            }
            else{
                flag = 8;
            }
        }
        *retcode = flag;
    }
    else{
        printf("Nothing returned in the return code of KC000. Setting this as an error.\n");
        *retcode = 8;
    }
}

1 个答案:

答案 0 :(得分:0)

我自己找到了问题的答案。以下是我为摆脱这种未定义的行为所做的工作:

#include "jni_funct.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

JavaVM *jvm;
JNIEnv *env;

int invokeKc000(kc0InputParms kc0inp, kc0ReturnParms *kc0ret)
{
    //JavaVM *jvm;
    //JNIEnv *env;
    kc0InputParms input;
    kc0ReturnParms *kc000out;
    char *kc910Path, *kc910Lib;
    int retcode;
    retcode = 8;

    //Rest of the codes

我将JNIEnvJavaVM作为全局变量而不是本地变量。

我仍然不明白为什么堆栈的分配导致了这种未定义的行为,而数据段的分配没有引起这样的问题。我将发布另一个有关此更改的问题。

感谢@Martin Zabel提供的暗示。