/* * Copyright (c) 1999, 2008, 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 #include #include #include #include "sysShmem.h" #include "shmemBase.h" #include "jdwpTransport.h" /* for Packet, TransportCallback */ #define MIN(x,y) ((x)<(y)?(x):(y)) /* * This is the base shared memory transport implementation that is used * by both front-end transports (through com.sun.tools.jdi) and * back-end transports (through JDWP_OnLoad and the function tables * it requires). It supports multiple connections for the benefit of the * front-end client; the back end interface assumes only a single connection. */ #define MAX_IPC_PREFIX 50 /* user-specified or generated name for */ /* shared memory seg and prefix for other IPC */ #define MAX_IPC_SUFFIX 25 /* suffix to shmem name for other IPC names */ #define MAX_IPC_NAME (MAX_IPC_PREFIX + MAX_IPC_SUFFIX) #define MAX_GENERATION_RETRIES 20 #define SHARED_BUFFER_SIZE 5000 #define CHECK_ERROR(expr) do { \ jint error = (expr); \ if (error != SYS_OK) { \ setLastError(error); \ return error; \ } \ } while (0) /* * The following assertions should hold anytime the stream's mutex is not held */ #define STREAM_INVARIANT(stream) \ do { \ SHMEM_ASSERT((stream->shared->readOffset < SHARED_BUFFER_SIZE) \ && (stream->shared->readOffset >= 0)); \ SHMEM_ASSERT((stream->shared->writeOffset < SHARED_BUFFER_SIZE) \ && (stream->shared->writeOffset >= 0)); \ } while (0) /* * Transports are duplex, so carve the shared memory into "streams", * one used to send from client to server, the other vice versa. */ typedef struct SharedMemoryListener { char mutexName[MAX_IPC_NAME]; char acceptEventName[MAX_IPC_NAME]; char attachEventName[MAX_IPC_NAME]; jboolean isListening; jboolean isAccepted; jlong acceptingPID; jlong attachingPID; } SharedListener; typedef struct SharedMemoryTransport { char name[MAX_IPC_PREFIX]; sys_ipmutex_t mutex; sys_event_t acceptEvent; sys_event_t attachEvent; sys_shmem_t sharedMemory; SharedListener *shared; } SharedMemoryTransport; /* * Access must be syncronized. Holds one shared * memory buffer and its state. */ typedef struct SharedStream { char mutexName[MAX_IPC_NAME]; char hasDataEventName[MAX_IPC_NAME]; char hasSpaceEventName[MAX_IPC_NAME]; int readOffset; int writeOffset; jboolean isFull; jbyte buffer[SHARED_BUFFER_SIZE]; } SharedStream; /* * The two shared streams: client to server and * server to client. */ typedef struct SharedMemory { SharedStream toClient; SharedStream toServer; } SharedMemory; /* * Local (to process) access to the shared memory * stream. access to hasData and hasSpace synchronized * by OS. */ typedef struct Stream { sys_ipmutex_t mutex; sys_event_t hasData; sys_event_t hasSpace; SharedStream *shared; jint state; } Stream; /* * Values for Stream.state field above. */ #define STATE_CLOSED 0xDEAD #define STATE_OPEN (STATE_CLOSED -1) /* * State checking macro. We compare against the STATE_OPEN value so * that STATE_CLOSED and any other value will be considered closed. * This catches a freed Stream as long as the memory page is still * valid. If the memory page is gone, then there is little that we * can do. */ #define IS_STATE_CLOSED(state) (state != STATE_OPEN) typedef struct SharedMemoryConnection { char name[MAX_IPC_NAME]; SharedMemory *shared; sys_shmem_t sharedMemory; Stream incoming; Stream outgoing; sys_process_t otherProcess; sys_event_t shutdown; /* signalled to indicate shutdown */ } SharedMemoryConnection; static jdwpTransportCallback *callback; static JavaVM *jvm; static int tlsIndex; typedef jint (*CreateFunc)(char *name, void *arg); /* * Set the per-thread error message (if not already set) */ static void setLastErrorMsg(char *newmsg) { char *msg; msg = (char *)sysTlsGet(tlsIndex); if (msg == NULL) { msg = (*callback->alloc)((int)strlen(newmsg)+1); if (msg != NULL) { strcpy(msg, newmsg); } sysTlsPut(tlsIndex, (void *)msg); } } /* * Clear last per-thread error message */ static void clearLastError() { char* msg = (char *)sysTlsGet(tlsIndex); if (msg != NULL) { (*callback->free)(msg); sysTlsPut(tlsIndex, NULL); } } /* * Set the per-thread error message to the textual representation * of the last system error (if not already set) */ static void setLastError(jint error) { char buf[128]; switch (error) { case SYS_OK : return; /* no-op */ case SYS_DIED : strcpy(buf, "Other process terminated"); break; case SYS_TIMEOUT : strcpy(buf, "Timed out"); break; default : sysGetLastError(buf, sizeof(buf)); } setLastErrorMsg(buf); } jint shmemBase_initialize(JavaVM *vm, jdwpTransportCallback *cbPtr) { jvm = vm; callback = cbPtr; tlsIndex = sysTlsAlloc(); return SYS_OK; } static jint createWithGeneratedName(char *prefix, char *nameBuffer, CreateFunc func, void *arg) { jint error; jint i = 0; do { strcpy(nameBuffer, prefix); if (i > 0) { char buf[10]; sprintf(buf, ".%d", i+1); strcat(nameBuffer, buf); } error = func(nameBuffer, arg); i++; } while ((error == SYS_INUSE) && (i < MAX_GENERATION_RETRIES)); if (error != SYS_OK) { setLastError(error); } return error; } typedef struct SharedMemoryArg { jint size; sys_shmem_t memory; void *start; } SharedMemoryArg; static jint createSharedMem(char *name, void *ptr) { SharedMemoryArg *arg = ptr; return sysSharedMemCreate(name, arg->size, &arg->memory, &arg->start); } static jint createMutex(char *name, void *arg) { sys_ipmutex_t *retArg = arg; return sysIPMutexCreate(name, retArg); } /* * Creates named or unnamed event that is automatically reset * (in other words, no need to reset event after it has signalled * a thread). */ static jint createEvent(char *name, void *arg) { sys_event_t *retArg = arg; return sysEventCreate(name, retArg, JNI_FALSE); } #define ADD_OFFSET(o1, o2) ((o1 + o2) % SHARED_BUFFER_SIZE) #define FULL(stream) (stream->shared->isFull) #define EMPTY(stream) ((stream->shared->writeOffset == stream->shared->readOffset) \ && !stream->shared->isFull) static jint leaveMutex(Stream *stream) { return sysIPMutexExit(stream->mutex); } /* enter the stream's mutex and (optionally) check for a closed stream */ static jint enterMutex(Stream *stream, sys_event_t event) { jint ret = sysIPMutexEnter(stream->mutex, event); if (ret != SYS_OK) { if (IS_STATE_CLOSED(stream->state)) { setLastErrorMsg("stream closed"); } return ret; } if (IS_STATE_CLOSED(stream->state)) { setLastErrorMsg("stream closed"); (void)leaveMutex(stream); return SYS_ERR; } return SYS_OK; } /* * Enter/exit with stream mutex held. * On error, does not hold the stream mutex. */ static jint waitForSpace(SharedMemoryConnection *connection, Stream *stream) { jint error = SYS_OK; /* Assumes mutex is held on call */ while ((error == SYS_OK) && FULL(stream)) { CHECK_ERROR(leaveMutex(stream)); error = sysEventWait(connection->otherProcess, stream->hasSpace, 0); if (error == SYS_OK) { CHECK_ERROR(enterMutex(stream, connection->shutdown)); } else { setLastError(error); } } return error; } static jint signalSpace(Stream *stream) { return sysEventSignal(stream->hasSpace); } /* * Enter/exit with stream mutex held. * On error, does not hold the stream mutex. */ static jint waitForData(SharedMemoryConnection *connection, Stream *stream) { jint error = SYS_OK; /* Assumes mutex is held on call */ while ((error == SYS_OK) && EMPTY(stream)) { CHECK_ERROR(leaveMutex(stream)); error = sysEventWait(connection->otherProcess, stream->hasData, 0); if (error == SYS_OK) { CHECK_ERROR(enterMutex(stream, connection->shutdown)); } else { setLastError(error); } } return error; } static jint signalData(Stream *stream) { return sysEventSignal(stream->hasData); } static jint closeStream(Stream *stream, jboolean linger) { /* * Lock stream during close - ignore shutdown event as we are * closing down and shutdown should be signalled. */ CHECK_ERROR(enterMutex(stream, NULL)); /* mark the stream as closed */ stream->state = STATE_CLOSED; /* wake up waitForData() if it is in sysEventWait() */ sysEventSignal(stream->hasData); sysEventClose(stream->hasData); /* wake up waitForSpace() if it is in sysEventWait() */ sysEventSignal(stream->hasSpace); sysEventClose(stream->hasSpace); /* * If linger requested then give the stream a few seconds to * drain before closing it. */ if (linger) { int attempts = 10; while (!EMPTY(stream) && attempts>0) { CHECK_ERROR(leaveMutex(stream)); sysSleep(200); CHECK_ERROR(enterMutex(stream, NULL)); attempts--; } } CHECK_ERROR(leaveMutex(stream)); sysIPMutexClose(stream->mutex); return SYS_OK; } /* * Server creates stream. */ static int createStream(char *name, Stream *stream) { jint error; char prefix[MAX_IPC_PREFIX]; sprintf(prefix, "%s.mutex", name); error = createWithGeneratedName(prefix, stream->shared->mutexName, createMutex, &stream->mutex); if (error != SYS_OK) { return error; } sprintf(prefix, "%s.hasData", name); error = createWithGeneratedName(prefix, stream->shared->hasDataEventName, createEvent, &stream->hasData); if (error != SYS_OK) { (void)closeStream(stream, JNI_FALSE); return error; } sprintf(prefix, "%s.hasSpace", name); error = createWithGeneratedName(prefix, stream->shared->hasSpaceEventName, createEvent, &stream->hasSpace); if (error != SYS_OK) { (void)closeStream(stream, JNI_FALSE); return error; } stream->shared->readOffset = 0; stream->shared->writeOffset = 0; stream->shared->isFull = JNI_FALSE; stream->state = STATE_OPEN; return SYS_OK; } /* * Initialization for the stream opened by the other process */ static int openStream(Stream *stream) { jint error; CHECK_ERROR(sysIPMutexOpen(stream->shared->mutexName, &stream->mutex)); error = sysEventOpen(stream->shared->hasDataEventName, &stream->hasData); if (error != SYS_OK) { setLastError(error); (void)closeStream(stream, JNI_FALSE); return error; } error = sysEventOpen(stream->shared->hasSpaceEventName, &stream->hasSpace); if (error != SYS_OK) { setLastError(error); (void)closeStream(stream, JNI_FALSE); return error; } stream->state = STATE_OPEN; return SYS_OK; } /********************************************************************/ static SharedMemoryConnection * allocConnection(void) { /* * TO DO: Track all allocated connections for clean shutdown? */ SharedMemoryConnection *conn = (*callback->alloc)(sizeof(SharedMemoryConnection)); if (conn != NULL) { memset(conn, 0, sizeof(SharedMemoryConnection)); } return conn; } static void freeConnection(SharedMemoryConnection *connection) { (*callback->free)(connection); } static void closeConnection(SharedMemoryConnection *connection) { /* * Signal all threads accessing this connection that we are * shutting down. */ if (connection->shutdown) { sysEventSignal(connection->shutdown); } (void)closeStream(&connection->outgoing, JNI_TRUE); (void)closeStream(&connection->incoming, JNI_FALSE); if (connection->sharedMemory) { sysSharedMemClose(connection->sharedMemory, connection->shared); } if (connection->otherProcess) { sysProcessClose(connection->otherProcess); } /* * Ideally we should close the connection->shutdown event and * free the connection structure. However as closing the * connection is asynchronous it means that other threads may * still be accessing the connection structure. On Win32 this * means we leak 132 bytes and one event per connection. This * memory will be reclaim at process exit. * * if (connection->shutdown) * sysEventClose(connection->shutdown); * freeConnection(connection); */ } /* * For client: connect to the shared memory. Open incoming and * outgoing streams. */ static jint openConnection(SharedMemoryTransport *transport, jlong otherPID, SharedMemoryConnection **connectionPtr) { jint error; SharedMemoryConnection *connection = allocConnection(); if (connection == NULL) { return SYS_NOMEM; } sprintf(connection->name, "%s.%ld", transport->name, (jint)sysProcessGetID()); error = sysSharedMemOpen(connection->name, &connection->sharedMemory, &connection->shared); if (error != SYS_OK) { closeConnection(connection); return error; } /* This process is the client */ connection->incoming.shared = &connection->shared->toClient; connection->outgoing.shared = &connection->shared->toServer; error = openStream(&connection->incoming); if (error != SYS_OK) { closeConnection(connection); return error; } error = openStream(&connection->outgoing); if (error != SYS_OK) { closeConnection(connection); return error; } error = sysProcessOpen(otherPID, &connection->otherProcess); if (error != SYS_OK) { setLastError(error); closeConnection(connection); return error; } /* * Create an event that signals that the connection is shutting * down. The event is unnamed as it's process local, and is * manually reset (so that signalling the event will signal * all threads waiting on it). */ error = sysEventCreate(NULL, &connection->shutdown, JNI_TRUE); if (error != SYS_OK) { setLastError(error); closeConnection(connection); return error; } *connectionPtr = connection; return SYS_OK; } /* * For server: create the shared memory. Create incoming and * outgoing streams. */ static jint createConnection(SharedMemoryTransport *transport, jlong otherPID, SharedMemoryConnection **connectionPtr) { jint error; char streamPrefix[MAX_IPC_NAME]; SharedMemoryConnection *connection = allocConnection(); if (connection == NULL) { return SYS_NOMEM; } sprintf(connection->name, "%s.%ld", transport->name, (jint)otherPID); error = sysSharedMemCreate(connection->name, sizeof(SharedMemory), &connection->sharedMemory, &connection->shared); if (error != SYS_OK) { closeConnection(connection); return error; } memset(connection->shared, 0, sizeof(SharedMemory)); /* This process is the server */ connection->incoming.shared = &connection->shared->toServer; connection->outgoing.shared = &connection->shared->toClient; strcpy(streamPrefix, connection->name); strcat(streamPrefix, ".ctos"); error = createStream(streamPrefix, &connection->incoming); if (error != SYS_OK) { closeConnection(connection); return error; } strcpy(streamPrefix, connection->name); strcat(streamPrefix, ".stoc"); error = createStream(streamPrefix, &connection->outgoing); if (error != SYS_OK) { closeConnection(connection); return error; } error = sysProcessOpen(otherPID, &connection->otherProcess); if (error != SYS_OK) { setLastError(error); closeConnection(connection); return error; } /* * Create an event that signals that the connection is shutting * down. The event is unnamed as it's process local, and is * manually reset (so that a signalling the event will signal * all threads waiting on it). */ error = sysEventCreate(NULL, &connection->shutdown, JNI_TRUE); if (error != SYS_OK) { setLastError(error); closeConnection(connection); return error; } *connectionPtr = connection; return SYS_OK; } /********************************************************************/ static SharedMemoryTransport * allocTransport(void) { /* * TO DO: Track all allocated transports for clean shutdown? */ return (*callback->alloc)(sizeof(SharedMemoryTransport)); } static void freeTransport(SharedMemoryTransport *transport) { (*callback->free)(transport); } static void closeTransport(SharedMemoryTransport *transport) { sysIPMutexClose(transport->mutex); sysEventClose(transport->acceptEvent); sysEventClose(transport->attachEvent); sysSharedMemClose(transport->sharedMemory, transport->shared); freeTransport(transport); } static int openTransport(const char *address, SharedMemoryTransport **transportPtr) { jint error; SharedMemoryTransport *transport; transport = allocTransport(); if (transport == NULL) { return SYS_NOMEM; } memset(transport, 0, sizeof(*transport)); if (strlen(address) >= MAX_IPC_PREFIX) { char buf[128]; sprintf(buf, "Error: address strings longer than %d characters are invalid\n", MAX_IPC_PREFIX); setLastErrorMsg(buf); closeTransport(transport); return SYS_ERR; } error = sysSharedMemOpen(address, &transport->sharedMemory, &transport->shared); if (error != SYS_OK) { setLastError(error); closeTransport(transport); return error; } strcpy(transport->name, address); error = sysIPMutexOpen(transport->shared->mutexName, &transport->mutex); if (error != SYS_OK) { setLastError(error); closeTransport(transport); return error; } error = sysEventOpen(transport->shared->acceptEventName, &transport->acceptEvent); if (error != SYS_OK) { setLastError(error); closeTransport(transport); return error; } error = sysEventOpen(transport->shared->attachEventName, &transport->attachEvent); if (error != SYS_OK) { setLastError(error); closeTransport(transport); return error; } *transportPtr = transport; return SYS_OK; } static jint createTransport(const char *address, SharedMemoryTransport **transportPtr) { SharedMemoryTransport *transport; jint error; char prefix[MAX_IPC_PREFIX]; transport = allocTransport(); if (transport == NULL) { return SYS_NOMEM; } memset(transport, 0, sizeof(*transport)); if ((address == NULL) || (address[0] == '\0')) { SharedMemoryArg arg; arg.size = sizeof(SharedListener); error = createWithGeneratedName("javadebug", transport->name, createSharedMem, &arg); transport->shared = arg.start; transport->sharedMemory = arg.memory; } else { if (strlen(address) >= MAX_IPC_PREFIX) { char buf[128]; sprintf(buf, "Error: address strings longer than %d characters are invalid\n", MAX_IPC_PREFIX); setLastErrorMsg(buf); closeTransport(transport); return SYS_ERR; } strcpy(transport->name, address); error = sysSharedMemCreate(address, sizeof(SharedListener), &transport->sharedMemory, &transport->shared); } if (error != SYS_OK) { setLastError(error); closeTransport(transport); return error; } memset(transport->shared, 0, sizeof(SharedListener)); transport->shared->acceptingPID = sysProcessGetID(); sprintf(prefix, "%s.mutex", transport->name); error = createWithGeneratedName(prefix, transport->shared->mutexName, createMutex, &transport->mutex); if (error != SYS_OK) { closeTransport(transport); return error; } sprintf(prefix, "%s.accept", transport->name); error = createWithGeneratedName(prefix, transport->shared->acceptEventName, createEvent, &transport->acceptEvent); if (error != SYS_OK) { closeTransport(transport); return error; } sprintf(prefix, "%s.attach", transport->name); error = createWithGeneratedName(prefix, transport->shared->attachEventName, createEvent, &transport->attachEvent); if (error != SYS_OK) { closeTransport(transport); return error; } *transportPtr = transport; return SYS_OK; } jint shmemBase_listen(const char *address, SharedMemoryTransport **transportPtr) { int error; clearLastError(); error = createTransport(address, transportPtr); if (error == SYS_OK) { (*transportPtr)->shared->isListening = JNI_TRUE; } return error; } jint shmemBase_accept(SharedMemoryTransport *transport, long timeout, SharedMemoryConnection **connectionPtr) { jint error; SharedMemoryConnection *connection; clearLastError(); CHECK_ERROR(sysEventWait(NULL, transport->attachEvent, timeout)); error = createConnection(transport, transport->shared->attachingPID, &connection); if (error != SYS_OK) { /* * Reject the attacher */ transport->shared->isAccepted = JNI_FALSE; sysEventSignal(transport->acceptEvent); freeConnection(connection); return error; } transport->shared->isAccepted = JNI_TRUE; error = sysEventSignal(transport->acceptEvent); if (error != SYS_OK) { /* * No real point trying to reject it. */ closeConnection(connection); return error; } *connectionPtr = connection; return SYS_OK; } static jint doAttach(SharedMemoryTransport *transport, long timeout) { transport->shared->attachingPID = sysProcessGetID(); CHECK_ERROR(sysEventSignal(transport->attachEvent)); CHECK_ERROR(sysEventWait(NULL, transport->acceptEvent, timeout)); return SYS_OK; } jint shmemBase_attach(const char *addressString, long timeout, SharedMemoryConnection **connectionPtr) { int error; SharedMemoryTransport *transport; jlong acceptingPID; clearLastError(); error = openTransport(addressString, &transport); if (error != SYS_OK) { return error; } /* lock transport - no additional event to wait on as no connection yet */ error = sysIPMutexEnter(transport->mutex, NULL); if (error != SYS_OK) { setLastError(error); closeTransport(transport); return error; } if (transport->shared->isListening) { error = doAttach(transport, timeout); if (error == SYS_OK) { acceptingPID = transport->shared->acceptingPID; } } else { /* Not listening: error */ error = SYS_ERR; } sysIPMutexExit(transport->mutex); if (error != SYS_OK) { closeTransport(transport); return error; } error = openConnection(transport, acceptingPID, connectionPtr); closeTransport(transport); return error; } void shmemBase_closeConnection(SharedMemoryConnection *connection) { clearLastError(); closeConnection(connection); } void shmemBase_closeTransport(SharedMemoryTransport *transport) { clearLastError(); closeTransport(transport); } jint shmemBase_sendByte(SharedMemoryConnection *connection, jbyte data) { Stream *stream = &connection->outgoing; SharedStream *shared = stream->shared; int offset; clearLastError(); CHECK_ERROR(enterMutex(stream, connection->shutdown)); CHECK_ERROR(waitForSpace(connection, stream)); SHMEM_ASSERT(!FULL(stream)); offset = shared->writeOffset; shared->buffer[offset] = data; shared->writeOffset = ADD_OFFSET(offset, 1); shared->isFull = (shared->readOffset == shared->writeOffset); STREAM_INVARIANT(stream); CHECK_ERROR(leaveMutex(stream)); CHECK_ERROR(signalData(stream)); return SYS_OK; } jint shmemBase_receiveByte(SharedMemoryConnection *connection, jbyte *data) { Stream *stream = &connection->incoming; SharedStream *shared = stream->shared; int offset; clearLastError(); CHECK_ERROR(enterMutex(stream, connection->shutdown)); CHECK_ERROR(waitForData(connection, stream)); SHMEM_ASSERT(!EMPTY(stream)); offset = shared->readOffset; *data = shared->buffer[offset]; shared->readOffset = ADD_OFFSET(offset, 1); shared->isFull = JNI_FALSE; STREAM_INVARIANT(stream); CHECK_ERROR(leaveMutex(stream)); CHECK_ERROR(signalSpace(stream)); return SYS_OK; } static jint sendBytes(SharedMemoryConnection *connection, const void *bytes, jint length) { Stream *stream = &connection->outgoing; SharedStream *shared = stream->shared; jint fragmentStart; jint fragmentLength; jint index = 0; jint maxLength; clearLastError(); CHECK_ERROR(enterMutex(stream, connection->shutdown)); while (index < length) { CHECK_ERROR(waitForSpace(connection, stream)); SHMEM_ASSERT(!FULL(stream)); fragmentStart = shared->writeOffset; if (fragmentStart < shared->readOffset) { maxLength = shared->readOffset - fragmentStart; } else { maxLength = SHARED_BUFFER_SIZE - fragmentStart; } fragmentLength = MIN(maxLength, length - index); memcpy(shared->buffer + fragmentStart, (jbyte *)bytes + index, fragmentLength); shared->writeOffset = ADD_OFFSET(fragmentStart, fragmentLength); index += fragmentLength; shared->isFull = (shared->readOffset == shared->writeOffset); STREAM_INVARIANT(stream); CHECK_ERROR(signalData(stream)); } CHECK_ERROR(leaveMutex(stream)); return SYS_OK; } /* * Send packet header followed by data. */ jint shmemBase_sendPacket(SharedMemoryConnection *connection, const jdwpPacket *packet) { jint data_length; clearLastError(); CHECK_ERROR(sendBytes(connection, &packet->type.cmd.id, sizeof(jint))); CHECK_ERROR(sendBytes(connection, &packet->type.cmd.flags, sizeof(jbyte))); if (packet->type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) { CHECK_ERROR(sendBytes(connection, &packet->type.reply.errorCode, sizeof(jshort))); } else { CHECK_ERROR(sendBytes(connection, &packet->type.cmd.cmdSet, sizeof(jbyte))); CHECK_ERROR(sendBytes(connection, &packet->type.cmd.cmd, sizeof(jbyte))); } data_length = packet->type.cmd.len - 11; SHMEM_GUARANTEE(data_length >= 0); CHECK_ERROR(sendBytes(connection, &data_length, sizeof(jint))); if (data_length > 0) { CHECK_ERROR(sendBytes(connection, packet->type.cmd.data, data_length)); } return SYS_OK; } static jint receiveBytes(SharedMemoryConnection *connection, void *bytes, jint length) { Stream *stream = &connection->incoming; SharedStream *shared = stream->shared; jint fragmentStart; jint fragmentLength; jint index = 0; jint maxLength; clearLastError(); CHECK_ERROR(enterMutex(stream, connection->shutdown)); while (index < length) { CHECK_ERROR(waitForData(connection, stream)); SHMEM_ASSERT(!EMPTY(stream)); fragmentStart = shared->readOffset; if (fragmentStart < shared->writeOffset) { maxLength = shared->writeOffset - fragmentStart; } else { maxLength = SHARED_BUFFER_SIZE - fragmentStart; } fragmentLength = MIN(maxLength, length - index); memcpy((jbyte *)bytes + index, shared->buffer + fragmentStart, fragmentLength); shared->readOffset = ADD_OFFSET(fragmentStart, fragmentLength); index += fragmentLength; shared->isFull = JNI_FALSE; STREAM_INVARIANT(stream); CHECK_ERROR(signalSpace(stream)); } CHECK_ERROR(leaveMutex(stream)); return SYS_OK; } /* * Read packet header and insert into packet structure. * Allocate space for the data and fill it in. */ jint shmemBase_receivePacket(SharedMemoryConnection *connection, jdwpPacket *packet) { jint data_length; jint error; clearLastError(); CHECK_ERROR(receiveBytes(connection, &packet->type.cmd.id, sizeof(jint))); CHECK_ERROR(receiveBytes(connection, &packet->type.cmd.flags, sizeof(jbyte))); if (packet->type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) { CHECK_ERROR(receiveBytes(connection, &packet->type.reply.errorCode, sizeof(jshort))); } else { CHECK_ERROR(receiveBytes(connection, &packet->type.cmd.cmdSet, sizeof(jbyte))); CHECK_ERROR(receiveBytes(connection, &packet->type.cmd.cmd, sizeof(jbyte))); } CHECK_ERROR(receiveBytes(connection, &data_length, sizeof(jint))); if (data_length < 0) { return SYS_ERR; } else if (data_length == 0) { packet->type.cmd.len = 11; packet->type.cmd.data = NULL; } else { packet->type.cmd.len = data_length + 11; packet->type.cmd.data = (*callback->alloc)(data_length); if (packet->type.cmd.data == NULL) { return SYS_ERR; } error = receiveBytes(connection, packet->type.cmd.data, data_length); if (error != SYS_OK) { (*callback->free)(packet->type.cmd.data); return error; } } return SYS_OK; } jint shmemBase_name(struct SharedMemoryTransport *transport, char **name) { *name = transport->name; return SYS_OK; } jint shmemBase_getlasterror(char *msg, jint size) { char *errstr = (char *)sysTlsGet(tlsIndex); if (errstr != NULL) { strcpy(msg, errstr); return SYS_OK; } else { return SYS_ERR; } } void exitTransportWithError(char *message, char *fileName, char *date, int lineNumber) { JNIEnv *env; jint error; char buffer[500]; sprintf(buffer, "Shared Memory Transport \"%s\" (%s), line %d: %s\n", fileName, date, lineNumber, message); error = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2); if (error != JNI_OK) { /* * We're forced into a direct call to exit() */ fprintf(stderr, "%s", buffer); exit(-1); } else { (*env)->FatalError(env, buffer); } }