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.
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. –
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
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. –