2010-04-06 5 views
0

Je rencontre une corruption de chaîne étrange à travers les appels JNI qui cause des problèmes sur le côté Java. De temps en temps, je vais obtenir une chaîne corrompue dans le tableau transmis, qui a parfois des parties existantes de la chaîne non corrompue d'origine. Le code C++ original était censé définir le premier index du tableau à l'adresse. La deuxième version utilise un tampon direct, parce que j'essayais de résoudre le problème. Le simulateur s'exécute dans un thread distinct des threads de l'application, les threads de l'application affichant des événements à exécuter. Je pensais avant, aussi, que parce que je préallouais le tampon, il pourrait être utilisé plus d'une fois si plusieurs threads accèdent au socket, causant ainsi une corruption, donc je l'ai changé pour un IoBuffer Mina qui est alloué d'une piscine, avec le support ByteBuffer disponible. Cependant, cela ne semble pas avoir fait de différence.JNI String Corruption

remoteaddress[0]: 10.1.1.2:49153 
remoteaddress[0]: 10.1.4.2:49153 
remoteaddress[0]: 10.1.6.2:49153 
remoteaddress[0]: 10.1.2.2:49153 
remoteaddress[0]: 10.1.9.2:49153 
remoteaddress[0]: {garbage here} 
java.lang.NullPointerException 
    at kokuks.KKSAddress.<init>(KKSAddress.java:139) 
    at kokuks.KKSAddress.createAddress(KKSAddress.java:48) 
    at kokuks.KKSSocket._recvFrom(KKSSocket.java:963) 
    at kokuks.scheduler.RecvOperation$1.execute(RecvOperation.java:144) 
    at kokuks.scheduler.RecvOperation$1.execute(RecvOperation.java:1) 
    at kokuks.KKSEvent.run(KKSEvent.java:58) 
    at kokuks.KokuKS.handleJNIEventExpiry(KokuKS.java:872) 
    at kokuks.KokuKS.handleJNIEventExpiry_fjni(KokuKS.java:880) 
    at kokuks.KokuKS.runSimulator_jni(Native Method) 
    at kokuks.KokuKS$1.run(KokuKS.java:773) 
    at java.lang.Thread.run(Thread.java:717) 
remoteaddress[0]: 10.1.7.2:49153 

L'exception du pointeur null provient de l'utilisation de la chaîne endommagée. En C++, l'adresse s'imprime normalement, mais cela réduit le taux d'erreurs, d'après ce que je peux voir.

Le code C++:

/* 
* Class:  kokuks_KKSSocket 
* Method: recvFrom2_jni 
* Signature: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;IIJ)I 
*/ 
JNIEXPORT jint JNICALL Java_kokuks_KKSSocket_recvFrom2_1jni 
(JNIEnv *env, jobject obj, jstring sockpath, jobject addrbuf, jobject buf, jint position, jint limit, jlong flags) { 

    const char* cstr = env->GetStringUTFChars(sockpath, NULL); 
    std::string spath = std::string(cstr); 
    env->ReleaseStringUTFChars(sockpath, cstr); // release me! 

    if (KKS_DEBUG) { 
     std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << std::endl; 
    } 

    ns3::Ptr<ns3::Object> sockobj = refmap[spath]; 
    ns3::Ptr<ns3::Socket> socket = ns3::DynamicCast<ns3::Socket>(sockobj); 
    if (!socket) { 
     std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " socket not found for path!!" << std::endl; 
     return -1; // not found 
    } 

    if (!addrbuf) { 
     std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " sender address directbuffer address is null!" << std::endl; 
     return -1; 
    } 

    uint8_t* bufaddr = (uint8_t*)env->GetDirectBufferAddress(buf); 
    long bufcap = env->GetDirectBufferCapacity(buf); 
    uint8_t* realbufaddr = bufaddr + position; 
    uint32_t remaining = limit - position; 

    uint8_t* addrbufaddr = (uint8_t*)env->GetDirectBufferAddress(addrbuf); 
    long addrbufcap = env->GetDirectBufferCapacity(buf); 

    if (KKS_DEBUG) { 
     std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " bufaddr: " << bufaddr << ", cap: " << bufcap << std::endl; 
    } 

    ns3::Address aaddr; 
    uint32_t mflags = flags; 

    int ret = socket->RecvFrom(realbufaddr, remaining, mflags, aaddr); 

    if (ret > 0) { 
     if (KKS_DEBUG) { 
      std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " addr: " << aaddr << std::endl; 
     } 
     ns3::InetSocketAddress insa = ns3::InetSocketAddress::ConvertFrom(aaddr); 

     std::stringstream ss; 
     insa.GetIpv4().Print(ss); 
     ss << ":" << insa.GetPort() << std::ends; 

     if (KKS_DEBUG) { 
      std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " addr: " << ss.str() << std::endl; 
     } 

     const char *cstr = ss.str().c_str(); 
     char *dst = (char*)addrbufaddr; 
     size_t len = strlen(cstr); 
     strncpy(dst, cstr, len + 1); 

     if (env->ExceptionOccurred()) { 
      env->ExceptionDescribe(); 
     } 
    } 

    jint jret = ret; 

    return jret; 
} 

/* 
* Class:  kokuks_KKSNode 
* Method: node_getID_jni 
* Signature: (Ljava/lang/String;)I 
*/ 
JNIEXPORT jint JNICALL Java_kokuks_KKSNode_node_1getID_1jni 
(JNIEnv *env, jobject obj, jstring path) { 

    const char* cstr = env->GetStringUTFChars(path, NULL); 
    std::string spath = std::string(cstr); 
    env->ReleaseStringUTFChars(path, cstr); // release me! 

    if (KKS_DEBUG) { 
     std::cout << "[kks-c~" << spath << "?] " << __PRETTY_FUNCTION__ << std::endl; 
    } 

    ns3::Ptr<ns3::Object> nodeobj = refmap[spath]; 
    ns3::Ptr<ns3::Node> node = ns3::DynamicCast<ns3::Node>(nodeobj); 
    if (node) { 
     uint32_t id = node->GetId(); 
     jint  j_id = id; 
     return j_id; 
    } 

    return -1; 
} 

Le code Java (si elle aide):

/** 
* 
* @param remoteaddress 
* @param bytes 
* @param flags 
* @return 
*/ 
protected int _core_recvFrom(final KKSAddress[] remoteaddress, final ByteBuffer bytes, final long flags) throws IOException { 
    if (!kks.isRealtime() || kks.isSimulationThread()) { 
     return _core_recvFrom_st(remoteaddress, bytes, flags); 
    } 

    boolean usejnibb = !bytes.isDirect(); 
    final IoBuffer iob; 

    final ByteBuffer mybuf; 
    if (usejnibb) { 
     if (USE_IOB) { 
      iob = IoBuffer.allocate(bytes.remaining(), true); 
      mybuf = iob.buf(); 
     } else { 
      mybuf = jnibb; 
     } 
     mybuf.clear(); 
     mybuf.limit(bytes.remaining()); 
    } else { 
     mybuf = bytes; 
     iob = null; 
    } 
    try { 
     KKSEvent<Integer> kev = new KKSSocketEvent<Integer>(this) { 
      @Override 
      protected Integer execute(long timeMS) throws IOException { 
       return _core_recvFrom_st(remoteaddress, mybuf, flags); 
      } 

      /* (non-Javadoc) 
      * @see kokuks.KKSEvent#getType() 
      */ 
      public String getType() { 
       return "_core_recvFrom()"; 
      } 
     }; 
     try { 
      int ret = kks.scheduleEventRTWait(kev); 
      if (ret > 0 && usejnibb) { 
       mybuf.flip(); 
       bytes.put(mybuf); 
      } 
      return ret; 
     } catch (InterruptedException e) { 
      throw new InterruptedIOException(); 
     } catch (EventExecException e) { 
      if (e.getCause() instanceof IOException) { 
       throw (IOException)e.getCause(); 
      } 
      throw new IOException(e.getCause()); 
     } catch (Exception e) { 
      throw new IOException(e); 
     } 
    } finally { 
     if (usejnibb) { 
      if (USE_IOB) { 
       iob.free(); 
      } 
     } 
    } 
} 

/** 
* Pass an array of size 1 into remote address, and this will be set with 
* the sender of the packet (hax). This emulates C++ references. 
* 
* @param remoteaddress 
* @param buf 
* @param flags 
* @return 
*/ 
protected int _core_recvFrom_st(final KKSAddress[] remoteaddress, ByteBuffer buf, long flags) throws IOException { 
    try { 
     _syncJNI(); 

     boolean recvfrom = remoteaddress != null; 

     errNo = SocketErrno.ERROR_NOTERROR; 

     ByteBuffer mybuf = buf; 

     if (!buf.isDirect()) { 
      errNo = SocketErrno.ERROR_BUFFERNOTDIRECT; 
      throw new IllegalArgumentException("Buffer not direct!"); 
     } 

     final IoBuffer iob; 
     ByteBuffer bb = null; 
     if (recvfrom) { 
      if (USE_IOB) { 
       iob = IoBuffer.allocate(128, true); 
       bb = iob.buf(); 
      } else { 
       bb = addrbb; 
      } 
      bb.clear(); 
     } else { 
      iob = null; 
     } 

     try { 

      //IoBuffer pre = IoBuffer.wrap(mybuf.duplicate()); 

      //printMessage("sockrecv (pre) // rxavailable: " + getRxAvailable()); 

      // use new mechanism 
      int ret = recvfrom ? 
       recvFrom2_jni(
       path.toPortableString(), 
       bb, 
       mybuf, 
       mybuf.position(), 
       mybuf.limit(), 
       flags 
      ) : recv_jni(
       path.toPortableString(), 
       mybuf, 
       mybuf.position(), 
       mybuf.limit(), 
       flags 
      ); 

      _syncJNI(); 

      if (ret >= 0) { 
       rxTotal += ret; 

       /* 
       printMessage("local addr: " + LOCAL_ADDR + ", real local addr: " + getRemoteAddress().toNormalAddress()); 
       printMessage("remote addr: " + REMOTE_ADDR + ", real remote addr: " + getApp().getNode().getIPV4Address()); 


       if (
        getType() == SOCKET_TYPE_TCP && 
        getRemoteAddress().toNormalAddress().equals(LOCAL_ADDR) && 
        getApp().getNode().getIPV4Address().equals(REMOTE_ADDR) 
       ) { 
        mrTest_testRecvd(mybuf, ret); 
       } 
       */ 

       //printMessage("sockrecv // mybuf: " + mybuf + ", ret: " + ret + " rxavailable: " + getRxAvailable() + ", data: " + BufUtils.asText(mybuf, ret)); 

       buf.position(buf.position() + ret); 

       if (recvfrom) { 
        String st; 
        try { 
         st = IoBuffer.wrap(bb).getString(CDE); 
         remoteaddress[0] = KKSAddress.createAddress(st); 
         if (remoteaddress[0] == null) { 
          System.out.println("warning; remote address is null!! original: " + st); 
         } 
        } catch (CharacterCodingException e) { 
         e.printStackTrace(); 
        } 
       } 

       return ret; 

       //pre.limit(pre.position() + ret); 
       //printMessage("_core_recvFrom_st recvd from " + ((!recvfrom || remoteaddress[0] == null || remoteaddress == null) ? getRemoteAddress() : remoteaddress[0]) + ": " + pre.getHexDump()); 
      } 
      throw new IOException("I/O exception, retval: " + ret + ", errNo: " + errNo); 
     } finally { 
      if (recvfrom) { 
       if (USE_IOB) { 
        iob.free(); 
       } 
      } 
     } 
    } finally { 
     errNo = _getErrNo(); 
    } 
} 

Edit: J'ai aussi identifié que les données de paquets a été corrompus. En outre, le problème se produit sur mon PC de travail plus que mon PC à la maison. Mon PC de travail fonctionne sous Windows XP, possède 4 Go de RAM et a un Q6600, mon PC à la maison a un Q6600 overclocké, 4 Go de RAM, et fonctionne sous Windows 7 64 bits, bien qu'il soit avec Java 32 bits.

+0

Je rencontre toujours ce problème, un an après, dans la même méthode (plus ou moins). Vraiment énervant. La sécurité des threads est totale, et ma connaissance des aspects de threading a décuplé. Cependant, j'ai toujours ce problème. Je doute que cela ait quelque chose à voir avec les discussions. Toutes les variables sont sur la pile. –

+0

Je vois pas mal de problèmes avec le code, 'addrbb' est partagé, donc vous aimeriez que ce soit un ThreadLocal . Vous ne vérifiez pas le résultat NULL dans le code C (mais cela devrait être mineur) et généralement la façon stanadard de passer ByteBuffer est de traiter ByteBuffer.address comme 'void *' dans C. Encore une note: ByteBuffer dans java sont au moins une taille de page (généralement 4k) allouée. 'ByteBuffer.duplicate' crée simplement un objet java dans la même zone de sauvegarde sous-jacente, donc inutile dans votre cas, vous pouvez utiliser ByteBuffer.slice si vous ne voulez pas conserver d'espace d'adressage. – bestsss

+0

IoBuffer utilise un pool d'allocation ThreadLocal ByteBuffer :) Je pense que le problème aurait pu être d'utiliser une valeur Java directement au lieu de faire la conversion sur une ligne différente. long bufcap = env-> GetDirectBufferCapacity (buf); uint8_t * realbufaddr = bufaddr + position; J'ai vu des problèmes dans le passé où j'ai essayé de le faire sur une seule ligne, et l'application échoue (surtout pour les valeurs de retour, cependant). jlong ​​j_bufcap = env-> GetDirectBufferCapacity (buf); long bufcap = j_bufcap; uint8_t * realbufaddr = bufaddr + position; Peut l'avoir corrigé, mais d'autres tests sont nécessaires. –

Répondre

0

Je n'ai pas testé votre code, mais je suppose qu'il s'agit d'un problème d'encodage de caractères. Essayez d'utiliser std::wstring au lieu de std::string ou quelque chose comme ça. Désolé je ne peux pas aider beaucoup en ce moment, mais cela devrait vous donner un point de départ, je suppose.

+0

Je ne sais pas, pourquoi me donnerait-il des cordes à ordures de temps en temps plutôt que tout le temps? C'est peut-être un problème d'allocation de mémoire. Très étrange. –

0

Les problèmes de sécurité des threads ne pouvaient-ils pas survenir? Essayez de synchroniser l'accès aux méthodes natives.

+0

Je pense que cela règle le problème, mais JNI n'est-il pas supposé être sûr pour les threads? –

+0

Je ne sais vraiment pas si JNI est thread-safe, mais si la DLL C++ n'est pas, toute la solution ne sera pas thread-safe. – Juliano

+0

Hmm, eh bien, la DLL C++ doit être thread-safe, il y a un mutex sur les fonctions d'événements contenues dans lequel sont appelés du côté Java. Je suppose que cela laisse seulement le mécanisme JNI qui pourrait avoir le problème. –