/* * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "util.h" #include "stream.h" #include "outStream.h" #include "inStream.h" #include "transport.h" #include "commonRef.h" #include "bag.h" #include "FrameID.h" #define INITIAL_ID_ALLOC 50 #define SMALLEST(a, b) ((a) < (b)) ? (a) : (b) static void commonInit(PacketOutputStream *stream) { stream->current = &stream->initialSegment[0]; stream->left = sizeof(stream->initialSegment); stream->segment = &stream->firstSegment; stream->segment->length = 0; stream->segment->data = &stream->initialSegment[0]; stream->segment->next = NULL; stream->error = JDWP_ERROR(NONE); stream->sent = JNI_FALSE; stream->ids = bagCreateBag(sizeof(jlong), INITIAL_ID_ALLOC); if (stream->ids == NULL) { stream->error = JDWP_ERROR(OUT_OF_MEMORY); } } void outStream_initCommand(PacketOutputStream *stream, jint id, jbyte flags, jbyte commandSet, jbyte command) { commonInit(stream); /* * Command-specific initialization */ stream->packet.type.cmd.id = id; stream->packet.type.cmd.cmdSet = commandSet; stream->packet.type.cmd.cmd = command; stream->packet.type.cmd.flags = flags; } void outStream_initReply(PacketOutputStream *stream, jint id) { commonInit(stream); /* * Reply-specific initialization */ stream->packet.type.reply.id = id; stream->packet.type.reply.errorCode = 0x0; stream->packet.type.cmd.flags = (jbyte)JDWPTRANSPORT_FLAGS_REPLY; } jint outStream_id(PacketOutputStream *stream) { return stream->packet.type.cmd.id; } jbyte outStream_command(PacketOutputStream *stream) { /* Only makes sense for commands */ JDI_ASSERT(!(stream->packet.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY)); return stream->packet.type.cmd.cmd; } static jdwpError writeBytes(PacketOutputStream *stream, void *source, int size) { jbyte *bytes = (jbyte *)source; if (stream->error) { return stream->error; } while (size > 0) { jint count; if (stream->left == 0) { jint segSize = SMALLEST(2 * stream->segment->length, MAX_SEGMENT_SIZE); jbyte *newSeg = jvmtiAllocate(segSize); struct PacketData *newHeader = jvmtiAllocate(sizeof(*newHeader)); if ((newSeg == NULL) || (newHeader == NULL)) { jvmtiDeallocate(newSeg); jvmtiDeallocate(newHeader); stream->error = JDWP_ERROR(OUT_OF_MEMORY); return stream->error; } newHeader->length = 0; newHeader->data = newSeg; newHeader->next = NULL; stream->segment->next = newHeader; stream->segment = newHeader; stream->current = newHeader->data; stream->left = segSize; } count = SMALLEST(size, stream->left); (void)memcpy(stream->current, bytes, count); stream->current += count; stream->left -= count; stream->segment->length += count; size -= count; bytes += count; } return JDWP_ERROR(NONE); } jdwpError outStream_writeBoolean(PacketOutputStream *stream, jboolean val) { jbyte byte = (val != 0) ? 1 : 0; return writeBytes(stream, &byte, sizeof(byte)); } jdwpError outStream_writeByte(PacketOutputStream *stream, jbyte val) { return writeBytes(stream, &val, sizeof(val)); } jdwpError outStream_writeChar(PacketOutputStream *stream, jchar val) { val = HOST_TO_JAVA_CHAR(val); return writeBytes(stream, &val, sizeof(val)); } jdwpError outStream_writeShort(PacketOutputStream *stream, jshort val) { val = HOST_TO_JAVA_SHORT(val); return writeBytes(stream, &val, sizeof(val)); } jdwpError outStream_writeInt(PacketOutputStream *stream, jint val) { val = HOST_TO_JAVA_INT(val); return writeBytes(stream, &val, sizeof(val)); } jdwpError outStream_writeLong(PacketOutputStream *stream, jlong val) { val = HOST_TO_JAVA_LONG(val); return writeBytes(stream, &val, sizeof(val)); } jdwpError outStream_writeFloat(PacketOutputStream *stream, jfloat val) { val = HOST_TO_JAVA_FLOAT(val); return writeBytes(stream, &val, sizeof(val)); } jdwpError outStream_writeDouble(PacketOutputStream *stream, jdouble val) { val = HOST_TO_JAVA_DOUBLE(val); return writeBytes(stream, &val, sizeof(val)); } jdwpError outStream_writeObjectTag(JNIEnv *env, PacketOutputStream *stream, jobject val) { return outStream_writeByte(stream, specificTypeKey(env, val)); } jdwpError outStream_writeObjectRef(JNIEnv *env, PacketOutputStream *stream, jobject val) { jlong id; jlong *idPtr; if (stream->error) { return stream->error; } if (val == NULL) { id = NULL_OBJECT_ID; } else { /* Convert the object to an object id */ id = commonRef_refToID(env, val); if (id == NULL_OBJECT_ID) { stream->error = JDWP_ERROR(OUT_OF_MEMORY); return stream->error; } /* Track the common ref in case we need to release it on a future error */ idPtr = bagAdd(stream->ids); if (idPtr == NULL) { commonRef_release(env, id); stream->error = JDWP_ERROR(OUT_OF_MEMORY); return stream->error; } else { *idPtr = id; } /* Add the encoded object id to the stream */ id = HOST_TO_JAVA_LONG(id); } return writeBytes(stream, &id, sizeof(id)); } jdwpError outStream_writeFrameID(PacketOutputStream *stream, FrameID val) { /* * Not good - we're writing a pointer as a jint. Need * to write as a jlong if sizeof(FrameID) == 8. */ if (sizeof(FrameID) == 8) { /*LINTED*/ return outStream_writeLong(stream, (jlong)val); } else { /*LINTED*/ return outStream_writeInt(stream, (jint)val); } } jdwpError outStream_writeMethodID(PacketOutputStream *stream, jmethodID val) { /* * Not good - we're writing a pointer as a jint. Need * to write as a jlong if sizeof(jmethodID) == 8. */ if (sizeof(jmethodID) == 8) { /*LINTED*/ return outStream_writeLong(stream, (jlong)(intptr_t)val); } else { /*LINTED*/ return outStream_writeInt(stream, (jint)(intptr_t)val); } } jdwpError outStream_writeFieldID(PacketOutputStream *stream, jfieldID val) { /* * Not good - we're writing a pointer as a jint. Need * to write as a jlong if sizeof(jfieldID) == 8. */ if (sizeof(jfieldID) == 8) { /*LINTED*/ return outStream_writeLong(stream, (jlong)(intptr_t)val); } else { /*LINTED*/ return outStream_writeInt(stream, (jint)(intptr_t)val); } } jdwpError outStream_writeLocation(PacketOutputStream *stream, jlocation val) { return outStream_writeLong(stream, (jlong)val); } jdwpError outStream_writeByteArray(PacketOutputStream*stream, jint length, jbyte *bytes) { (void)outStream_writeInt(stream, length); return writeBytes(stream, bytes, length); } jdwpError outStream_writeString(PacketOutputStream *stream, char *string) { jdwpError error; jint length = string != NULL ? (int)strlen(string) : 0; /* Options utf8=y/n controls if we want Standard UTF-8 or Modified */ if ( gdata->modifiedUtf8 ) { (void)outStream_writeInt(stream, length); error = writeBytes(stream, (jbyte *)string, length); } else { jint new_length; new_length = (gdata->npt->utf8mToUtf8sLength) (gdata->npt->utf, (jbyte*)string, length); if ( new_length == length ) { (void)outStream_writeInt(stream, length); error = writeBytes(stream, (jbyte *)string, length); } else { char *new_string; new_string = jvmtiAllocate(new_length+1); (gdata->npt->utf8mToUtf8s) (gdata->npt->utf, (jbyte*)string, length, (jbyte*)new_string, new_length); (void)outStream_writeInt(stream, new_length); error = writeBytes(stream, (jbyte *)new_string, new_length); jvmtiDeallocate(new_string); } } return error; } jdwpError outStream_writeValue(JNIEnv *env, PacketOutputStream *out, jbyte typeKey, jvalue value) { if (typeKey == JDWP_TAG(OBJECT)) { (void)outStream_writeByte(out, specificTypeKey(env, value.l)); } else { (void)outStream_writeByte(out, typeKey); } if (isObjectTag(typeKey)) { (void)outStream_writeObjectRef(env, out, value.l); } else { switch (typeKey) { case JDWP_TAG(BYTE): return outStream_writeByte(out, value.b); case JDWP_TAG(CHAR): return outStream_writeChar(out, value.c); case JDWP_TAG(FLOAT): return outStream_writeFloat(out, value.f); case JDWP_TAG(DOUBLE): return outStream_writeDouble(out, value.d); case JDWP_TAG(INT): return outStream_writeInt(out, value.i); case JDWP_TAG(LONG): return outStream_writeLong(out, value.j); case JDWP_TAG(SHORT): return outStream_writeShort(out, value.s); case JDWP_TAG(BOOLEAN): return outStream_writeBoolean(out, value.z); case JDWP_TAG(VOID): /* happens with function return values */ /* write nothing */ return JDWP_ERROR(NONE); default: EXIT_ERROR(AGENT_ERROR_INVALID_OBJECT,"Invalid type key"); break; } } return JDWP_ERROR(NONE); } jdwpError outStream_skipBytes(PacketOutputStream *stream, jint count) { int i; for (i = 0; i < count; i++) { (void)outStream_writeByte(stream, 0); } return stream->error; } jdwpError outStream_error(PacketOutputStream *stream) { return stream->error; } void outStream_setError(PacketOutputStream *stream, jdwpError error) { if (stream->error == JDWP_ERROR(NONE)) { stream->error = error; LOG_MISC(("outStream_setError error=%s(%d)", jdwpErrorText(error), error)); } } static jint outStream_send(PacketOutputStream *stream) { jint rc; jint len = 0; PacketData *segment; jbyte *data, *posP; /* * If there's only 1 segment then we just send the * packet. */ if (stream->firstSegment.next == NULL) { stream->packet.type.cmd.len = 11 + stream->firstSegment.length; stream->packet.type.cmd.data = stream->firstSegment.data; rc = transport_sendPacket(&stream->packet); return rc; } /* * Multiple segments */ len = 0; segment = (PacketData *)&(stream->firstSegment); do { len += segment->length; segment = segment->next; } while (segment != NULL); data = jvmtiAllocate(len); if (data == NULL) { return JDWP_ERROR(OUT_OF_MEMORY); } posP = data; segment = (PacketData *)&(stream->firstSegment); while (segment != NULL) { (void)memcpy(posP, segment->data, segment->length); posP += segment->length; segment = segment->next; } stream->packet.type.cmd.len = 11 + len; stream->packet.type.cmd.data = data; rc = transport_sendPacket(&stream->packet); stream->packet.type.cmd.data = NULL; jvmtiDeallocate(data); return rc; } void outStream_sendReply(PacketOutputStream *stream) { jint rc; if (stream->error) { /* * Don't send any collected stream data on an error reply */ stream->packet.type.reply.len = 0; stream->packet.type.reply.errorCode = (jshort)stream->error; } rc = outStream_send(stream); if (rc == 0) { stream->sent = JNI_TRUE; } } void outStream_sendCommand(PacketOutputStream *stream) { jint rc; if (!stream->error) { rc = outStream_send(stream); if (rc == 0) { stream->sent = JNI_TRUE; } } } static jboolean releaseID(void *elementPtr, void *arg) { jlong *idPtr = elementPtr; commonRef_release(getEnv(), *idPtr); return JNI_TRUE; } void outStream_destroy(PacketOutputStream *stream) { struct PacketData *next; if (stream->error || !stream->sent) { (void)bagEnumerateOver(stream->ids, releaseID, NULL); } next = stream->firstSegment.next; while (next != NULL) { struct PacketData *p = next; next = p->next; jvmtiDeallocate(p->data); jvmtiDeallocate(p); } bagDestroyBag(stream->ids); }