/* * TO DO: Support processing of replies through command input streams. */ void inStream_init(PacketInputStream *stream, jdwpPacket packet) { stream->packet = packet; stream->error = JDWP_ERROR(NONE); stream->left = packet.type.cmd.len; stream->current = packet.type.cmd.data; stream->refs = bagCreateBag(sizeof(jobject), INITIAL_REF_ALLOC); if (stream->refs == NULL) { stream->error = JDWP_ERROR(OUT_OF_MEMORY); } }
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); }
/* * Read an object from the stream. The ID used in the wire protocol * is converted to a reference which is returned. The reference is * global and strong, but it should *not* be deleted by the caller * since it is freed when this stream is destroyed. */ jobject inStream_readObjectRef(PacketInputStream *stream) { jobject ref; jobject *refPtr; jlong id = inStream_readLong(stream); if (stream->error) { return NULL; } if (id == NULL_OBJECT_ID) { return NULL; } ref = commonRef_idToRef(id); if (ref == NULL) { stream->error = JDWP_ERROR(INVALID_OBJECT); return NULL; } refPtr = bagAdd(stream->refs); if (refPtr == NULL) { commonRef_idToRef_delete(NULL, ref); return NULL; } *refPtr = ref; return ref; }
/** * This is the back-end implementation for disabling * (what are at the JDI level) EventRequests. */ static jboolean clearCommand(PacketInputStream *in, PacketOutputStream *out) { jvmtiError error; jdwpEvent eventType; HandlerID handlerID; EventIndex ei; eventType = inStream_readByte(in); if (inStream_error(in)) { return JNI_TRUE; } handlerID = inStream_readInt(in); if (inStream_error(in)) { return JNI_TRUE; } ei = jdwp2EventIndex(eventType); if (ei == 0) { /* NOTE: Clear command not yet spec'ed to return INVALID_EVENT_TYPE */ outStream_setError(out, JDWP_ERROR(INVALID_EVENT_TYPE)); return JNI_TRUE; } error = eventHandler_freeByID(ei, handlerID); if (error != JVMTI_ERROR_NONE) { outStream_setError(out, map2jdwpError(error)); } return JNI_TRUE; }
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_setError(PacketOutputStream *stream, jdwpError error) { if (stream->error == JDWP_ERROR(NONE)) { stream->error = error; LOG_MISC(("outStream_setError error=%s(%d)", jdwpErrorText(error), error)); } }
jvalue inStream_readValue(PacketInputStream *stream, jbyte *typeKeyPtr) { jvalue value; jbyte typeKey = inStream_readByte(stream); if (stream->error) { value.j = 0L; return value; } if (isObjectTag(typeKey)) { value.l = inStream_readObjectRef(stream); } else { switch (typeKey) { case JDWP_TAG(BYTE): value.b = inStream_readByte(stream); break; case JDWP_TAG(CHAR): value.c = inStream_readChar(stream); break; case JDWP_TAG(FLOAT): value.f = inStream_readFloat(stream); break; case JDWP_TAG(DOUBLE): value.d = inStream_readDouble(stream); break; case JDWP_TAG(INT): value.i = inStream_readInt(stream); break; case JDWP_TAG(LONG): value.j = inStream_readLong(stream); break; case JDWP_TAG(SHORT): value.s = inStream_readShort(stream); break; case JDWP_TAG(BOOLEAN): value.z = inStream_readBoolean(stream); break; default: stream->error = JDWP_ERROR(INVALID_TAG); break; } } if (typeKeyPtr) { *typeKeyPtr = typeKey; } return value; }
static jdwpError launch(char *command, char *name, char *address) { jint rc; char *buf; char *commandLine; int len; /* Construct complete command line (all in UTF-8) */ commandLine = jvmtiAllocate((int)strlen(command) + (int)strlen(name) + (int)strlen(address) + 3); if (commandLine == NULL) { return JDWP_ERROR(OUT_OF_MEMORY); } (void)strcpy(commandLine, command); (void)strcat(commandLine, " "); (void)strcat(commandLine, name); (void)strcat(commandLine, " "); (void)strcat(commandLine, address); /* Convert commandLine from UTF-8 to platform encoding */ len = (int)strlen(commandLine); buf = jvmtiAllocate(len*3+3); (void)(gdata->npt->utf8ToPlatform)(gdata->npt->utf, (jbyte*)commandLine, len, buf, len*3+3); /* Exec commandLine */ rc = dbgsysExec(buf); /* Free up buffers */ jvmtiDeallocate(buf); jvmtiDeallocate(commandLine); /* And non-zero exit status means we had an error */ if (rc != SYS_OK) { return JDWP_ERROR(TRANSPORT_INIT); } return JDWP_ERROR(NONE); }
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); }
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; }
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)); }
static void * newComponents(PacketOutputStream *out, jint length, size_t nbytes) { void *ptr = NULL; if ( length > 0 ) { ptr = jvmtiAllocate(length*((jint)nbytes)); if ( ptr == NULL ) { outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY)); } else { (void)memset(ptr, 0, length*nbytes); } } return ptr; }
jint eventFilter_setCountFilter(HandlerNode *node, jint index, jint count) { CountFilter *filter = &FILTER(node, index).u.Count; if (index >= FILTER_COUNT(node)) { return JVMDI_ERROR_ILLEGAL_ARGUMENT; } if (count <= 0) { return JDWP_ERROR(INVALID_COUNT); } else { FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(Count); filter->count = count; return JVMDI_ERROR_NONE; } }
jthreadGroup inStream_readThreadGroupRef(PacketInputStream *stream) { jobject object = inStream_readObjectRef(stream); if (object == NULL) { /* * Could be error or just the null reference. In either case, * stop now. */ return NULL; } if (!isThreadGroup(object)) { stream->error = JDWP_ERROR(INVALID_THREAD_GROUP); return NULL; } return object; }
jarray inStream_readArrayRef(PacketInputStream *stream) { jobject object = inStream_readObjectRef(stream); if (object == NULL) { /* * Could be error or just the null reference. In either case, * stop now. */ return NULL; } if (!isArray(object)) { stream->error = JDWP_ERROR(INVALID_ARRAY); return NULL; } return object; }
jthread inStream_readThreadRef(JNIEnv *env, PacketInputStream *stream) { jobject object = inStream_readObjectRef(env, stream); if (object == NULL) { /* * Could be error or just the null reference. In either case, * stop now. */ return NULL; } if (!isThread(object)) { stream->error = JDWP_ERROR(INVALID_THREAD); return NULL; } return object; }
jclass inStream_readClassLoaderRef(PacketInputStream *stream) { jobject object = inStream_readObjectRef(stream); if (object == NULL) { /* * Could be error or just the null reference. In either case, * stop now. */ return NULL; } if (!isClassLoader(object)) { stream->error = JDWP_ERROR(INVALID_CLASS_LOADER); return NULL; } return object; }
jstring inStream_readStringRef(PacketInputStream *stream) { jobject object = inStream_readObjectRef(stream); if (object == NULL) { /* * Could be error or just the null reference. In either case, * stop now. */ return NULL; } if (!isString(object)) { stream->error = JDWP_ERROR(INVALID_STRING); return NULL; } return object; }
static jdwpError readBytes(PacketInputStream *stream, void *dest, int size) { if (stream->error) { return stream->error; } if (size > stream->left) { stream->error = JDWP_ERROR(INTERNAL); return stream->error; } if (dest) { (void)memcpy(dest, stream->current, size); } stream->current += size; stream->left -= size; return stream->error; }
/* * Restore all static data to the initialized state so that another * debugger can connect properly later. */ void debugInit_reset(JNIEnv *env) { EnumerateArg arg; LOG_MISC(("debugInit_reset() beginning")); currentSessionID++; initComplete = JNI_FALSE; eventHandler_reset(currentSessionID); transport_reset(); debugDispatch_reset(); invoker_reset(); stepControl_reset(); threadControl_reset(); util_reset(); commonRef_reset(env); classTrack_reset(); /* * If this is a server, we are now ready to accept another connection. * If it's a client, then we've cleaned up some (more should be added * later) and we're done. */ if (isServer) { arg.isServer = JNI_TRUE; arg.error = JDWP_ERROR(NONE); arg.startCount = 0; (void)bagEnumerateOver(transports, startTransport, &arg); signalInitComplete(); transport_waitForConnection(); } else { signalInitComplete(); /* Why? */ } LOG_MISC(("debugInit_reset() completed.")); }
static jboolean startTransport(void *item, void *arg) { TransportSpec *transport = item; EnumerateArg *enumArg = arg; jdwpError serror; LOG_MISC(("Begin startTransport")); serror = transport_startTransport(enumArg->isServer, transport->name, transport->address, transport->timeout); if (serror != JDWP_ERROR(NONE)) { ERROR_MESSAGE(("JDWP Transport %s failed to initialize, %s(%d)", transport->name, jdwpErrorText(serror), serror)); enumArg->error = serror; } else { /* (Don't overwrite any previous error) */ enumArg->startCount++; } LOG_MISC(("End startTransport")); return JNI_TRUE; /* Always continue, even if there was an error */ }
void debugLoop_run(void) { jboolean shouldListen; jdwpPacket p; jvmtiStartFunction func; /* Initialize all statics */ /* We may be starting a new connection after an error */ cmdQueue = NULL; cmdQueueLock = debugMonitorCreate("JDWP Command Queue Lock"); transportError = JNI_FALSE; shouldListen = JNI_TRUE; func = &reader; (void)spawnNewThread(func, NULL, "JDWP Command Reader"); standardHandlers_onConnect(); threadControl_onConnect(); /* Okay, start reading cmds! */ while (shouldListen) { if (!dequeue(&p)) { break; } if (p.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) { /* * Its a reply packet. */ continue; } else { /* * Its a cmd packet. */ jdwpCmdPacket *cmd = &p.type.cmd; PacketInputStream in; PacketOutputStream out; CommandHandler func; /* Should reply be sent to sender. * For error handling, assume yes, since * only VM/exit does not reply */ jboolean replyToSender = JNI_TRUE; /* * For VirtualMachine.Resume commands we hold the resumeLock * while executing and replying to the command. This ensures * that a Resume after VM_DEATH will be allowed to complete * before the thread posting the VM_DEATH continues VM * termination. */ if (resumeCommand(cmd)) { debugMonitorEnter(resumeLock); } /* Initialize the input and output streams */ inStream_init(&in, p); outStream_initReply(&out, inStream_id(&in)); LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd)); func = debugDispatch_getHandler(cmd->cmdSet,cmd->cmd); if (func == NULL) { /* we've never heard of this, so I guess we * haven't implemented it. * Handle gracefully for future expansion * and platform / vendor expansion. */ outStream_setError(&out, JDWP_ERROR(NOT_IMPLEMENTED)); } else if (gdata->vmDead && ((cmd->cmdSet) != JDWP_COMMAND_SET(VirtualMachine))) { /* Protect the VM from calls while dead. * VirtualMachine cmdSet quietly ignores some cmds * after VM death, so, it sends it's own errors. */ outStream_setError(&out, JDWP_ERROR(VM_DEAD)); } else { /* Call the command handler */ replyToSender = func(&in, &out); } /* Reply to the sender */ if (replyToSender) { if (inStream_error(&in)) { outStream_setError(&out, inStream_error(&in)); } outStream_sendReply(&out); } /* * Release the resumeLock as the reply has been posted. */ if (resumeCommand(cmd)) { debugMonitorExit(resumeLock); } inStream_destroy(&in); outStream_destroy(&out); shouldListen = !lastCommand(cmd); } } threadControl_onDisconnect(); standardHandlers_onDisconnect(); /* * Cut off the transport immediately. This has the effect of * cutting off any events that the eventHelper thread might * be trying to send. */ transport_close(); debugMonitorDestroy(cmdQueueLock); /* Reset for a new connection to this VM if it's still alive */ if ( ! gdata->vmDead ) { debugInit_reset(getEnv()); } }
/** * Take JDWP "modifiers" (which are JDI explicit filters, like * addCountFilter(), and implicit filters, like the LocationOnly * filter that goes with breakpoints) and add them as filters * (eventFilter) to the HandlerNode (eventHandler). */ static jdwpError readAndSetFilters(JNIEnv *env, PacketInputStream *in, HandlerNode *node, jint filterCount) { int i; jdwpError serror = JDWP_ERROR(NONE); for (i = 0; i < filterCount; ++i) { jbyte modifier; modifier = inStream_readByte(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; switch (modifier) { case JDWP_REQUEST_MODIFIER(Conditional): { jint exprID; exprID = inStream_readInt(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setConditionalFilter(node, i, exprID)); break; } case JDWP_REQUEST_MODIFIER(Count): { jint count; count = inStream_readInt(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setCountFilter(node, i, count)); break; } case JDWP_REQUEST_MODIFIER(ThreadOnly): { jthread thread; thread = inStream_readThreadRef(env, in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setThreadOnlyFilter(node, i, thread)); break; } case JDWP_REQUEST_MODIFIER(LocationOnly): { jbyte tag; jclass clazz; jmethodID method; jlocation location; tag = inStream_readByte(in); /* not currently used */ tag = tag; /* To shut up lint */ if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; clazz = inStream_readClassRef(env, in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; method = inStream_readMethodID(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; location = inStream_readLocation(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setLocationOnlyFilter(node, i, clazz, method, location)); break; } case JDWP_REQUEST_MODIFIER(FieldOnly): { jclass clazz; jfieldID field; clazz = inStream_readClassRef(env, in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; field = inStream_readFieldID(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setFieldOnlyFilter(node, i, clazz, field)); break; } case JDWP_REQUEST_MODIFIER(ClassOnly): { jclass clazz; clazz = inStream_readClassRef(env, in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setClassOnlyFilter(node, i, clazz)); break; } case JDWP_REQUEST_MODIFIER(ExceptionOnly): { jclass exception; jboolean caught; jboolean uncaught; exception = inStream_readClassRef(env, in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; caught = inStream_readBoolean(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; uncaught = inStream_readBoolean(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setExceptionOnlyFilter(node, i, exception, caught, uncaught)); break; } case JDWP_REQUEST_MODIFIER(InstanceOnly): { jobject instance; instance = inStream_readObjectRef(env, in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setInstanceOnlyFilter(node, i, instance)); break; } case JDWP_REQUEST_MODIFIER(ClassMatch): { char *pattern; pattern = inStream_readString(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setClassMatchFilter(node, i, pattern)); break; } case JDWP_REQUEST_MODIFIER(ClassExclude): { char *pattern; pattern = inStream_readString(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setClassExcludeFilter(node, i, pattern)); break; } case JDWP_REQUEST_MODIFIER(Step): { jthread thread; jint size; jint depth; thread = inStream_readThreadRef(env, in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; size = inStream_readInt(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; depth = inStream_readInt(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) break; serror = map2jdwpError( eventFilter_setStepFilter(node, i, thread, size, depth)); break; } case JDWP_REQUEST_MODIFIER(SourceNameMatch): { char *sourceNamePattern; sourceNamePattern = inStream_readString(in); if ( (serror = inStream_error(in)) != JDWP_ERROR(NONE) ) { break; } serror = map2jdwpError( eventFilter_setSourceNameMatchFilter(node, i, sourceNamePattern)); break; } default: serror = JDWP_ERROR(ILLEGAL_ARGUMENT); break; } if ( serror != JDWP_ERROR(NONE) ) break; } return serror; }
/** * This is the back-end implementation for enabling * (what are at the JDI level) EventRequests. * * Allocate the event request handler (eventHandler). * Add any filters (explicit or implicit). * Install the handler. * Return the handlerID which is used to map subsequent * events to the EventRequest that created it. */ static jboolean setCommand(PacketInputStream *in, PacketOutputStream *out) { jdwpError serror; HandlerNode *node; HandlerID requestID = -1; jdwpEvent eventType; jbyte suspendPolicy; jint filterCount; EventIndex ei; node = NULL; eventType = inStream_readByte(in); if (inStream_error(in)) { return JNI_TRUE; } suspendPolicy = inStream_readByte(in); if (inStream_error(in)) { return JNI_TRUE; } filterCount = inStream_readInt(in); if (inStream_error(in)) { return JNI_TRUE; } ei = jdwp2EventIndex(eventType); if (ei == 0) { outStream_setError(out, JDWP_ERROR(INVALID_EVENT_TYPE)); return JNI_TRUE; } if (ei == EI_VM_INIT) { /* * VM is already initialized so there's no need to install a handler * for this event. However we need to allocate a requestID to send in * the reply to the debugger. */ serror = JDWP_ERROR(NONE); requestID = eventHandler_allocHandlerID(); } else { node = eventHandler_alloc(filterCount, ei, suspendPolicy); if (node == NULL) { outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY)); return JNI_TRUE; } if (eventType == JDWP_EVENT(METHOD_EXIT_WITH_RETURN_VALUE)) { node->needReturnValue = 1; } else { node->needReturnValue = 0; } serror = readAndSetFilters(getEnv(), in, node, filterCount); if (serror == JDWP_ERROR(NONE)) { jvmtiError error; error = eventHandler_installExternal(node); serror = map2jdwpError(error); if (serror == JDWP_ERROR(NONE)) { requestID = node->handlerID; } } } if (serror == JDWP_ERROR(NONE)) { (void)outStream_writeInt(out, requestID); } else { (void)eventHandler_free(node); outStream_setError(out, serror); } return JNI_TRUE; }
jdwpError transport_startTransport(jboolean isServer, char *name, char *address, long timeout) { jvmtiStartFunction func; jdwpTransportEnv *trans; char threadName[MAXPATHLEN + 100]; jint err; jdwpError serror; /* * If the transport is already loaded then use it * Note: We're assuming here that we don't support multiple * transports - when we do then we need to handle the case * where the transport library only supports a single environment. * That probably means we have a bag a transport environments * to correspond to the transports bag. */ if (transport != NULL) { trans = transport; } else { serror = loadTransport(name, &trans); if (serror != JDWP_ERROR(NONE)) { return serror; } } if (isServer) { char *retAddress; char *launchCommand; TransportInfo *info; jvmtiError error; int len; char* prop_value; info = jvmtiAllocate(sizeof(*info)); if (info == NULL) { return JDWP_ERROR(OUT_OF_MEMORY); } info->name = jvmtiAllocate((int)strlen(name)+1); (void)strcpy(info->name, name); info->address = NULL; info->timeout = timeout; if (info->name == NULL) { serror = JDWP_ERROR(OUT_OF_MEMORY); goto handleError; } if (address != NULL) { info->address = jvmtiAllocate((int)strlen(address)+1); (void)strcpy(info->address, address); if (info->address == NULL) { serror = JDWP_ERROR(OUT_OF_MEMORY); goto handleError; } } info->transport = trans; err = (*trans)->StartListening(trans, address, &retAddress); if (err != JDWPTRANSPORT_ERROR_NONE) { printLastError(trans, err); serror = JDWP_ERROR(TRANSPORT_INIT); goto handleError; } /* * Record listener address in a system property */ len = (int)strlen(name) + (int)strlen(retAddress) + 2; /* ':' and '\0' */ prop_value = (char*)jvmtiAllocate(len); strcpy(prop_value, name); strcat(prop_value, ":"); strcat(prop_value, retAddress); setTransportProperty(getEnv(), prop_value); jvmtiDeallocate(prop_value); (void)strcpy(threadName, "JDWP Transport Listener: "); (void)strcat(threadName, name); func = &acceptThread; error = spawnNewThread(func, (void*)info, threadName); if (error != JVMTI_ERROR_NONE) { serror = map2jdwpError(error); goto handleError; } launchCommand = debugInit_launchOnInit(); if (launchCommand != NULL) { serror = launch(launchCommand, name, retAddress); if (serror != JDWP_ERROR(NONE)) { goto handleError; } } else { if ( ! gdata->quiet ) { TTY_MESSAGE(("Listening for transport %s at address: %s", name, retAddress)); } } return JDWP_ERROR(NONE); handleError: jvmtiDeallocate(info->name); jvmtiDeallocate(info->address); jvmtiDeallocate(info); } else { /* * Note that we don't attempt to do a launch here. Launching * is currently supported only in server mode. */ /* * If we're connecting to another process, there shouldn't be * any concurrent listens, so its ok if we block here in this * thread, waiting for the attach to finish. */ err = (*trans)->Attach(trans, address, timeout, 0); if (err != JDWPTRANSPORT_ERROR_NONE) { printLastError(trans, err); serror = JDWP_ERROR(TRANSPORT_INIT); return serror; } /* * Start the transport loop in a separate thread */ (void)strcpy(threadName, "JDWP Transport Listener: "); (void)strcat(threadName, name); func = &attachThread; err = spawnNewThread(func, (void*)trans, threadName); serror = map2jdwpError(err); } return serror; }
/* * Initialize debugger back end modules */ static void initialize(JNIEnv *env, jthread thread, EventIndex triggering_ei) { jvmtiError error; EnumerateArg arg; jbyte suspendPolicy; LOG_MISC(("Begin initialize()")); currentSessionID = 0; initComplete = JNI_FALSE; if ( gdata->vmDead ) { EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM dead at initialize() time"); } /* Turn off the initial JVMTI event notifications */ error = set_event_notification(JVMTI_DISABLE, EI_EXCEPTION); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "unable to disable JVMTI event notification"); } error = set_event_notification(JVMTI_DISABLE, EI_VM_INIT); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "unable to disable JVMTI event notification"); } error = set_event_notification(JVMTI_DISABLE, EI_VM_DEATH); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "unable to disable JVMTI event notification"); } /* Remove initial event callbacks */ (void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks)); error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventCallbacks) (gdata->jvmti, &(gdata->callbacks), sizeof(gdata->callbacks)); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "unable to clear JVMTI callbacks"); } commonRef_initialize(); util_initialize(env); threadControl_initialize(); stepControl_initialize(); invoker_initialize(); debugDispatch_initialize(); classTrack_initialize(env); debugLoop_initialize(); initMonitor = debugMonitorCreate("JDWP Initialization Monitor"); /* * Initialize transports */ arg.isServer = isServer; arg.error = JDWP_ERROR(NONE); arg.startCount = 0; transport_initialize(); (void)bagEnumerateOver(transports, startTransport, &arg); /* * Exit with an error only if * 1) none of the transports was successfully started, and * 2) the application has not yet started running */ if ((arg.error != JDWP_ERROR(NONE)) && (arg.startCount == 0) && initOnStartup) { EXIT_ERROR(map2jvmtiError(arg.error), "No transports initialized"); } eventHandler_initialize(currentSessionID); signalInitComplete(); transport_waitForConnection(); suspendPolicy = suspendOnInit ? JDWP_SUSPEND_POLICY(ALL) : JDWP_SUSPEND_POLICY(NONE); if (triggering_ei == EI_VM_INIT) { LOG_MISC(("triggering_ei == EI_VM_INIT")); eventHelper_reportVMInit(env, currentSessionID, thread, suspendPolicy); } else { /* * TO DO: Kludgy way of getting the triggering event to the * just-attached debugger. It would be nice to make this a little * cleaner. There is also a race condition where other events * can get in the queue (from other not-yet-suspended threads) * before this one does. (Also need to handle allocation error below?) */ EventInfo info; struct bag *initEventBag; LOG_MISC(("triggering_ei != EI_VM_INIT")); initEventBag = eventHelper_createEventBag(); (void)memset(&info,0,sizeof(info)); info.ei = triggering_ei; eventHelper_recordEvent(&info, 0, suspendPolicy, initEventBag); (void)eventHelper_reportEvents(currentSessionID, initEventBag); bagDestroyBag(initEventBag); } if ( gdata->vmDead ) { EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM dead before initialize() completes"); } LOG_MISC(("End initialize()")); }
/* * loadTransport() is adapted from loadJVMHelperLib() in * JDK 1.2 javai.c v1.61 */ static jdwpError loadTransport(char *name, jdwpTransportEnv **transportPtr) { JNIEnv *env; jdwpTransport_OnLoad_t onLoad; void *handle; char *libdir; /* Make sure library name is not empty */ if (name == NULL) { ERROR_MESSAGE(("library name is empty")); return JDWP_ERROR(TRANSPORT_LOAD); } /* First, look in sun.boot.library.path. This should find the standard * dt_socket and dt_shmem transport libraries, or any library * that was delivered with the J2SE. * Note: Since 6819213 fixed, Java property sun.boot.library.path can * contain multiple paths. Dll_dir is the first entry and * -Dsun.boot.library.path entries are appended. */ libdir = gdata->property_sun_boot_library_path; if (libdir == NULL) { ERROR_MESSAGE(("Java property sun.boot.library.path is not set")); return JDWP_ERROR(TRANSPORT_LOAD); } handle = loadTransportLibrary(libdir, name); if (handle == NULL) { /* Second, look along the path used by the native dlopen/LoadLibrary * functions. This should effectively try and load the simple * library name, which will cause the default system library * search technique to happen. * We should only reach here if the transport library wasn't found * in the J2SE directory, e.g. it's a custom transport library * not installed in the J2SE like dt_socket and dt_shmem is. * * Note: Why not use java.library.path? Several reasons: * a) This matches existing agentlib search * b) These are technically not JNI libraries */ handle = loadTransportLibrary("", name); } /* See if a library was found with this name */ if (handle == NULL) { ERROR_MESSAGE(("transport library not found: %s", name)); return JDWP_ERROR(TRANSPORT_LOAD); } /* Find the onLoad address */ onLoad = findTransportOnLoad(handle); if (onLoad == NULL) { ERROR_MESSAGE(("transport library missing onLoad entry: %s", name)); return JDWP_ERROR(TRANSPORT_LOAD); } /* Get transport interface */ env = getEnv(); if ( env != NULL ) { jdwpTransportEnv *t; JavaVM *jvm; jint ver; JNI_FUNC_PTR(env,GetJavaVM)(env, &jvm); ver = (*onLoad)(jvm, &callback, JDWPTRANSPORT_VERSION_1_0, &t); if (ver != JNI_OK) { switch (ver) { case JNI_ENOMEM : ERROR_MESSAGE(("insufficient memory to complete initialization")); break; case JNI_EVERSION : ERROR_MESSAGE(("transport doesn't recognize version %x", JDWPTRANSPORT_VERSION_1_0)); break; case JNI_EEXIST : ERROR_MESSAGE(("transport doesn't support multiple environments")); break; default: ERROR_MESSAGE(("unrecognized error %d from transport", ver)); break; } return JDWP_ERROR(TRANSPORT_INIT); } *transportPtr = t; } else { return JDWP_ERROR(TRANSPORT_LOAD); } return JDWP_ERROR(NONE); }
/* * Determine the component class by looking thru all classes for * one that has the signature of the component and the same class loadeer * as the array. See JVM spec 5.3.3: * If the component type is a reference type, C is marked as having * been defined by the defining class loader of the component type. */ static jdwpError getComponentClass(JNIEnv *env, jclass arrayClass, char *componentSignature, jclass *componentClassPtr) { jobject arrayClassLoader; jclass *classes; jint count; jclass componentClass = NULL; jdwpError serror; jvmtiError error; serror = JDWP_ERROR(NONE); error = classLoader(arrayClass, &arrayClassLoader); if (error != JVMTI_ERROR_NONE) { return map2jdwpError(error); } error = allLoadedClasses(&classes, &count); if (error != JVMTI_ERROR_NONE) { serror = map2jdwpError(error); } else { int i; for (i = 0; (i < count) && (componentClass == NULL); i++) { char *signature = NULL; jclass clazz = classes[i]; jboolean match; jvmtiError error; /* signature must match */ error = classSignature(clazz, &signature, NULL); if (error != JVMTI_ERROR_NONE) { serror = map2jdwpError(error); break; } match = strcmp(signature, componentSignature) == 0; jvmtiDeallocate(signature); /* if signature matches, get class loader to check if * it matches */ if (match) { jobject loader; error = classLoader(clazz, &loader); if (error != JVMTI_ERROR_NONE) { return map2jdwpError(error); } match = isSameObject(env, loader, arrayClassLoader); } if (match) { componentClass = clazz; } } jvmtiDeallocate(classes); *componentClassPtr = componentClass; } if (serror == JDWP_ERROR(NONE) && componentClass == NULL) { /* per JVM spec, component class is always loaded * before array class, so this should never occur. */ serror = JDWP_ERROR(NOT_FOUND); } return serror; }
void inStream_clearError(PacketInputStream *stream) { stream->error = JDWP_ERROR(NONE); }