| /* | |
| * Copyright (c) 1998, 2006, 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 "VirtualMachineImpl.h" | |
| #include "commonRef.h" | |
| #include "inStream.h" | |
| #include "outStream.h" | |
| #include "eventHandler.h" | |
| #include "eventHelper.h" | |
| #include "threadControl.h" | |
| #include "SDE.h" | |
| #include "FrameID.h" | |
| static char *versionName = "Java Debug Wire Protocol (Reference Implementation)"; | |
| static int majorVersion = 1; /* JDWP major version */ | |
| static int minorVersion = 8; /* JDWP minor version */ | |
| static jboolean | |
| version(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| char buf[500]; | |
| char *vmName; | |
| char *vmVersion; | |
| char *vmInfo; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| vmVersion = gdata->property_java_version; | |
| if (vmVersion == NULL) { | |
| vmVersion = "<unknown>"; | |
| } | |
| vmName = gdata->property_java_vm_name; | |
| if (vmName == NULL) { | |
| vmName = "<unknown>"; | |
| } | |
| vmInfo = gdata->property_java_vm_info; | |
| if (vmInfo == NULL) { | |
| vmInfo = "<unknown>"; | |
| } | |
| /* | |
| * Write the descriptive version information | |
| */ | |
| (void)snprintf(buf, sizeof(buf), | |
| "%s version %d.%d\nJVM Debug Interface version %d.%d\n" | |
| "JVM version %s (%s, %s)", | |
| versionName, majorVersion, minorVersion, | |
| jvmtiMajorVersion(), jvmtiMinorVersion(), | |
| vmVersion, vmName, vmInfo); | |
| (void)outStream_writeString(out, buf); | |
| /* | |
| * Write the JDWP version numbers | |
| */ | |
| (void)outStream_writeInt(out, majorVersion); | |
| (void)outStream_writeInt(out, minorVersion); | |
| /* | |
| * Write the VM version and name | |
| */ | |
| (void)outStream_writeString(out, vmVersion); | |
| (void)outStream_writeString(out, vmName); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| classesForSignature(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| JNIEnv *env; | |
| char *signature; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| signature = inStream_readString(in); | |
| if (signature == NULL) { | |
| outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY)); | |
| return JNI_TRUE; | |
| } | |
| if (inStream_error(in)) { | |
| return JNI_TRUE; | |
| } | |
| env = getEnv(); | |
| WITH_LOCAL_REFS(env, 1) { | |
| jint classCount; | |
| jclass *theClasses; | |
| jvmtiError error; | |
| error = allLoadedClasses(&theClasses, &classCount); | |
| if ( error == JVMTI_ERROR_NONE ) { | |
| /* Count classes in theClasses which match signature */ | |
| int matchCount = 0; | |
| /* Count classes written to the JDWP connection */ | |
| int writtenCount = 0; | |
| int i; | |
| for (i=0; i<classCount; i++) { | |
| jclass clazz = theClasses[i]; | |
| jint status = classStatus(clazz); | |
| char *candidate_signature = NULL; | |
| jint wanted = | |
| (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY| | |
| JVMTI_CLASS_STATUS_PRIMITIVE); | |
| /* We want prepared classes, primitives, and arrays only */ | |
| if ((status & wanted) == 0) { | |
| continue; | |
| } | |
| error = classSignature(clazz, &candidate_signature, NULL); | |
| if (error != JVMTI_ERROR_NONE) { | |
| break; | |
| } | |
| if (strcmp(candidate_signature, signature) == 0) { | |
| /* Float interesting classes (those that | |
| * are matching and are prepared) to the | |
| * beginning of the array. | |
| */ | |
| theClasses[i] = theClasses[matchCount]; | |
| theClasses[matchCount++] = clazz; | |
| } | |
| jvmtiDeallocate(candidate_signature); | |
| } | |
| /* At this point matching prepared classes occupy | |
| * indicies 0 thru matchCount-1 of theClasses. | |
| */ | |
| if ( error == JVMTI_ERROR_NONE ) { | |
| (void)outStream_writeInt(out, matchCount); | |
| for (; writtenCount < matchCount; writtenCount++) { | |
| jclass clazz = theClasses[writtenCount]; | |
| jint status = classStatus(clazz); | |
| jbyte tag = referenceTypeTag(clazz); | |
| (void)outStream_writeByte(out, tag); | |
| (void)outStream_writeObjectRef(env, out, clazz); | |
| (void)outStream_writeInt(out, map2jdwpClassStatus(status)); | |
| /* No point in continuing if there's an error */ | |
| if (outStream_error(out)) { | |
| break; | |
| } | |
| } | |
| } | |
| jvmtiDeallocate(theClasses); | |
| } | |
| if ( error != JVMTI_ERROR_NONE ) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| } | |
| } END_WITH_LOCAL_REFS(env); | |
| jvmtiDeallocate(signature); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| allClasses1(PacketInputStream *in, PacketOutputStream *out, int outputGenerics) | |
| { | |
| JNIEnv *env; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| env = getEnv(); | |
| WITH_LOCAL_REFS(env, 1) { | |
| jint classCount; | |
| jclass *theClasses; | |
| jvmtiError error; | |
| error = allLoadedClasses(&theClasses, &classCount); | |
| if ( error != JVMTI_ERROR_NONE ) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| } else { | |
| /* Count classes in theClasses which are prepared */ | |
| int prepCount = 0; | |
| /* Count classes written to the JDWP connection */ | |
| int writtenCount = 0; | |
| int i; | |
| for (i=0; i<classCount; i++) { | |
| jclass clazz = theClasses[i]; | |
| jint status = classStatus(clazz); | |
| jint wanted = | |
| (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY); | |
| /* We want prepared classes and arrays only */ | |
| if ((status & wanted) != 0) { | |
| /* Float interesting classes (those that | |
| * are prepared) to the beginning of the array. | |
| */ | |
| theClasses[i] = theClasses[prepCount]; | |
| theClasses[prepCount++] = clazz; | |
| } | |
| } | |
| /* At this point prepared classes occupy | |
| * indicies 0 thru prepCount-1 of theClasses. | |
| */ | |
| (void)outStream_writeInt(out, prepCount); | |
| for (; writtenCount < prepCount; writtenCount++) { | |
| char *signature = NULL; | |
| char *genericSignature = NULL; | |
| jclass clazz = theClasses[writtenCount]; | |
| jint status = classStatus(clazz); | |
| jbyte tag = referenceTypeTag(clazz); | |
| jvmtiError error; | |
| error = classSignature(clazz, &signature, &genericSignature); | |
| if (error != JVMTI_ERROR_NONE) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| break; | |
| } | |
| (void)outStream_writeByte(out, tag); | |
| (void)outStream_writeObjectRef(env, out, clazz); | |
| (void)outStream_writeString(out, signature); | |
| if (outputGenerics == 1) { | |
| writeGenericSignature(out, genericSignature); | |
| } | |
| (void)outStream_writeInt(out, map2jdwpClassStatus(status)); | |
| jvmtiDeallocate(signature); | |
| if (genericSignature != NULL) { | |
| jvmtiDeallocate(genericSignature); | |
| } | |
| /* No point in continuing if there's an error */ | |
| if (outStream_error(out)) { | |
| break; | |
| } | |
| } | |
| jvmtiDeallocate(theClasses); | |
| } | |
| } END_WITH_LOCAL_REFS(env); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| allClasses(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| return allClasses1(in, out, 0); | |
| } | |
| static jboolean | |
| allClassesWithGeneric(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| return allClasses1(in, out, 1); | |
| } | |
| /***********************************************************/ | |
| static jboolean | |
| instanceCounts(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| jint classCount; | |
| jclass *classes; | |
| JNIEnv *env; | |
| int ii; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| classCount = inStream_readInt(in); | |
| if (inStream_error(in)) { | |
| return JNI_TRUE; | |
| } | |
| if (classCount == 0) { | |
| (void)outStream_writeInt(out, 0); | |
| return JNI_TRUE; | |
| } | |
| if (classCount < 0) { | |
| outStream_setError(out, JDWP_ERROR(ILLEGAL_ARGUMENT)); | |
| return JNI_TRUE; | |
| } | |
| env = getEnv(); | |
| classes = jvmtiAllocate(classCount * (int)sizeof(jclass)); | |
| for (ii = 0; ii < classCount; ii++) { | |
| jdwpError errorCode; | |
| classes[ii] = inStream_readClassRef(env, in); | |
| errorCode = inStream_error(in); | |
| if (errorCode != JDWP_ERROR(NONE)) { | |
| /* | |
| * A class could have been unloaded/gc'd so | |
| * if we get an error, just ignore it and keep | |
| * going. An instanceCount of 0 will be returned. | |
| */ | |
| if (errorCode == JDWP_ERROR(INVALID_OBJECT) || | |
| errorCode == JDWP_ERROR(INVALID_CLASS)) { | |
| inStream_clearError(in); | |
| classes[ii] = NULL; | |
| continue; | |
| } | |
| jvmtiDeallocate(classes); | |
| return JNI_TRUE; | |
| } | |
| } | |
| WITH_LOCAL_REFS(env, 1) { | |
| jlong *counts; | |
| jvmtiError error; | |
| counts = jvmtiAllocate(classCount * (int)sizeof(jlong)); | |
| /* Iterate over heap getting info on these classes */ | |
| error = classInstanceCounts(classCount, classes, counts); | |
| if (error != JVMTI_ERROR_NONE) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| } else { | |
| (void)outStream_writeInt(out, classCount); | |
| for (ii = 0; ii < classCount; ii++) { | |
| (void)outStream_writeLong(out, counts[ii]); | |
| } | |
| } | |
| jvmtiDeallocate(counts); | |
| } END_WITH_LOCAL_REFS(env); | |
| jvmtiDeallocate(classes); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| redefineClasses(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| jvmtiClassDefinition *classDefs; | |
| jboolean ok = JNI_TRUE; | |
| jint classCount; | |
| jint i; | |
| JNIEnv *env; | |
| if (gdata->vmDead) { | |
| /* quietly ignore */ | |
| return JNI_TRUE; | |
| } | |
| classCount = inStream_readInt(in); | |
| if (inStream_error(in)) { | |
| return JNI_TRUE; | |
| } | |
| if ( classCount == 0 ) { | |
| return JNI_TRUE; | |
| } | |
| /*LINTED*/ | |
| classDefs = jvmtiAllocate(classCount*(int)sizeof(jvmtiClassDefinition)); | |
| if (classDefs == NULL) { | |
| outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY)); | |
| return JNI_TRUE; | |
| } | |
| /*LINTED*/ | |
| (void)memset(classDefs, 0, classCount*sizeof(jvmtiClassDefinition)); | |
| env = getEnv(); | |
| for (i = 0; i < classCount; ++i) { | |
| int byteCount; | |
| unsigned char * bytes; | |
| jclass clazz; | |
| clazz = inStream_readClassRef(env, in); | |
| if (inStream_error(in)) { | |
| ok = JNI_FALSE; | |
| break; | |
| } | |
| byteCount = inStream_readInt(in); | |
| if (inStream_error(in)) { | |
| ok = JNI_FALSE; | |
| break; | |
| } | |
| if ( byteCount <= 0 ) { | |
| outStream_setError(out, JDWP_ERROR(INVALID_CLASS_FORMAT)); | |
| ok = JNI_FALSE; | |
| break; | |
| } | |
| bytes = (unsigned char *)jvmtiAllocate(byteCount); | |
| if (bytes == NULL) { | |
| outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY)); | |
| ok = JNI_FALSE; | |
| break; | |
| } | |
| (void)inStream_readBytes(in, byteCount, (jbyte *)bytes); | |
| if (inStream_error(in)) { | |
| ok = JNI_FALSE; | |
| break; | |
| } | |
| classDefs[i].klass = clazz; | |
| classDefs[i].class_byte_count = byteCount; | |
| classDefs[i].class_bytes = bytes; | |
| } | |
| if (ok == JNI_TRUE) { | |
| jvmtiError error; | |
| error = JVMTI_FUNC_PTR(gdata->jvmti,RedefineClasses) | |
| (gdata->jvmti, classCount, classDefs); | |
| if (error != JVMTI_ERROR_NONE) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| } else { | |
| /* zap our BP info */ | |
| for ( i = 0 ; i < classCount; i++ ) { | |
| eventHandler_freeClassBreakpoints(classDefs[i].klass); | |
| } | |
| } | |
| } | |
| /* free up allocated memory */ | |
| for ( i = 0 ; i < classCount; i++ ) { | |
| if ( classDefs[i].class_bytes != NULL ) { | |
| jvmtiDeallocate((void*)classDefs[i].class_bytes); | |
| } | |
| } | |
| jvmtiDeallocate(classDefs); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| setDefaultStratum(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| char *stratumId; | |
| if (gdata->vmDead) { | |
| /* quietly ignore */ | |
| return JNI_TRUE; | |
| } | |
| stratumId = inStream_readString(in); | |
| if (inStream_error(in)) { | |
| return JNI_TRUE; | |
| } else if (strcmp(stratumId, "") == 0) { | |
| stratumId = NULL; | |
| } | |
| setGlobalStratumId(stratumId); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| getAllThreads(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| JNIEnv *env; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| env = getEnv(); | |
| WITH_LOCAL_REFS(env, 1) { | |
| int i; | |
| jint threadCount; | |
| jthread *theThreads; | |
| theThreads = allThreads(&threadCount); | |
| if (theThreads == NULL) { | |
| outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY)); | |
| } else { | |
| /* Squish out all of the debugger-spawned threads */ | |
| threadCount = filterDebugThreads(theThreads, threadCount); | |
| (void)outStream_writeInt(out, threadCount); | |
| for (i = 0; i <threadCount; i++) { | |
| (void)outStream_writeObjectRef(env, out, theThreads[i]); | |
| } | |
| jvmtiDeallocate(theThreads); | |
| } | |
| } END_WITH_LOCAL_REFS(env); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| topLevelThreadGroups(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| JNIEnv *env; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| env = getEnv(); | |
| WITH_LOCAL_REFS(env, 1) { | |
| jvmtiError error; | |
| jint groupCount; | |
| jthreadGroup *groups; | |
| groups = NULL; | |
| error = JVMTI_FUNC_PTR(gdata->jvmti,GetTopThreadGroups) | |
| (gdata->jvmti, &groupCount, &groups); | |
| if (error != JVMTI_ERROR_NONE) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| } else { | |
| int i; | |
| (void)outStream_writeInt(out, groupCount); | |
| for (i = 0; i < groupCount; i++) { | |
| (void)outStream_writeObjectRef(env, out, groups[i]); | |
| } | |
| jvmtiDeallocate(groups); | |
| } | |
| } END_WITH_LOCAL_REFS(env); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| dispose(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| idSizes(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| (void)outStream_writeInt(out, sizeof(jfieldID)); /* fields */ | |
| (void)outStream_writeInt(out, sizeof(jmethodID)); /* methods */ | |
| (void)outStream_writeInt(out, sizeof(jlong)); /* objects */ | |
| (void)outStream_writeInt(out, sizeof(jlong)); /* referent types */ | |
| (void)outStream_writeInt(out, sizeof(FrameID)); /* frames */ | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| suspend(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| jvmtiError error; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| error = threadControl_suspendAll(); | |
| if (error != JVMTI_ERROR_NONE) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| } | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| resume(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| jvmtiError error; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| error = threadControl_resumeAll(); | |
| if (error != JVMTI_ERROR_NONE) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| } | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| doExit(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| jint exitCode; | |
| exitCode = inStream_readInt(in); | |
| if (gdata->vmDead) { | |
| /* quietly ignore */ | |
| return JNI_FALSE; | |
| } | |
| /* We send the reply from here because we are about to exit. */ | |
| if (inStream_error(in)) { | |
| outStream_setError(out, inStream_error(in)); | |
| } | |
| outStream_sendReply(out); | |
| forceExit(exitCode); | |
| /* Shouldn't get here */ | |
| JDI_ASSERT(JNI_FALSE); | |
| /* Shut up the compiler */ | |
| return JNI_FALSE; | |
| } | |
| static jboolean | |
| createString(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| JNIEnv *env; | |
| char *cstring; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| cstring = inStream_readString(in); | |
| if (cstring == NULL) { | |
| outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY)); | |
| return JNI_TRUE; | |
| } | |
| if (inStream_error(in)) { | |
| return JNI_TRUE; | |
| } | |
| env = getEnv(); | |
| WITH_LOCAL_REFS(env, 1) { | |
| jstring string; | |
| string = JNI_FUNC_PTR(env,NewStringUTF)(env, cstring); | |
| if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) { | |
| outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY)); | |
| } else { | |
| (void)outStream_writeObjectRef(env, out, string); | |
| } | |
| } END_WITH_LOCAL_REFS(env); | |
| jvmtiDeallocate(cstring); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| capabilities(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| jvmtiCapabilities caps; | |
| jvmtiError error; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| error = jvmtiGetCapabilities(&caps); | |
| if (error != JVMTI_ERROR_NONE) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| return JNI_TRUE; | |
| } | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_field_modification_events); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_field_access_events); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_bytecodes); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_synthetic_attribute); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_owned_monitor_info); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_current_contended_monitor); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_monitor_info); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| capabilitiesNew(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| jvmtiCapabilities caps; | |
| jvmtiError error; | |
| if (gdata->vmDead) { | |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); | |
| return JNI_TRUE; | |
| } | |
| error = jvmtiGetCapabilities(&caps); | |
| if (error != JVMTI_ERROR_NONE) { | |
| outStream_setError(out, map2jdwpError(error)); | |
| return JNI_TRUE; | |
| } | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_field_modification_events); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_field_access_events); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_bytecodes); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_synthetic_attribute); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_owned_monitor_info); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_current_contended_monitor); | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_monitor_info); | |
| /* new since JDWP version 1.4 */ | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_redefine_classes); | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE /* can_add_method */ ); | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE /* can_unrestrictedly_redefine_classes */ ); | |
| /* 11: canPopFrames */ | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_pop_frame); | |
| /* 12: canUseInstanceFilters */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_TRUE); | |
| /* 13: canGetSourceDebugExtension */ | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_source_debug_extension); | |
| /* 14: canRequestVMDeathEvent */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_TRUE); | |
| /* 15: canSetDefaultStratum */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_TRUE); | |
| /* 16: canGetInstanceInfo */ | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_tag_objects); | |
| /* 17: canRequestMonitorEvents */ | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_monitor_events); | |
| /* 18: canGetMonitorFrameInfo */ | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_owned_monitor_stack_depth_info); | |
| /* remaining reserved */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 19 */ | |
| /* 20 Can get constant pool information */ | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_get_constant_pool); | |
| /* 21 Can force early return */ | |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_force_early_return); | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 22 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 23 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 24 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 25 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 26 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 27 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 28 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 29 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 30 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 31 */ | |
| (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 32 */ | |
| return JNI_TRUE; | |
| } | |
| static int | |
| countPaths(char *string) { | |
| int cnt = 1; /* always have one */ | |
| char *pos = string; | |
| char *ps; | |
| ps = gdata->property_path_separator; | |
| if ( ps == NULL ) { | |
| ps = ";"; | |
| } | |
| while ((pos = strchr(pos, ps[0])) != NULL) { | |
| ++cnt; | |
| ++pos; | |
| } | |
| return cnt; | |
| } | |
| static void | |
| writePaths(PacketOutputStream *out, char *string) { | |
| char *pos; | |
| char *ps; | |
| char *buf; | |
| int npaths; | |
| int i; | |
| buf = jvmtiAllocate((int)strlen(string)+1); | |
| npaths = countPaths(string); | |
| (void)outStream_writeInt(out, npaths); | |
| ps = gdata->property_path_separator; | |
| if ( ps == NULL ) { | |
| ps = ";"; | |
| } | |
| pos = string; | |
| for ( i = 0 ; i < npaths ; i++ ) { | |
| char *psPos; | |
| int plen; | |
| psPos = strchr(pos, ps[0]); | |
| if ( psPos == NULL ) { | |
| plen = (int)strlen(pos); | |
| } else { | |
| plen = (int)(psPos-pos); | |
| psPos++; | |
| } | |
| (void)memcpy(buf, pos, plen); | |
| buf[plen] = 0; | |
| (void)outStream_writeString(out, buf); | |
| pos = psPos; | |
| } | |
| jvmtiDeallocate(buf); | |
| } | |
| static jboolean | |
| classPaths(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| char *ud; | |
| char *bp; | |
| char *cp; | |
| ud = gdata->property_user_dir; | |
| if ( ud == NULL ) { | |
| ud = ""; | |
| } | |
| cp = gdata->property_java_class_path; | |
| if ( cp == NULL ) { | |
| cp = ""; | |
| } | |
| bp = gdata->property_sun_boot_class_path; | |
| if ( bp == NULL ) { | |
| bp = ""; | |
| } | |
| (void)outStream_writeString(out, ud); | |
| writePaths(out, cp); | |
| writePaths(out, bp); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| disposeObjects(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| int i; | |
| int refCount; | |
| jlong id; | |
| int requestCount; | |
| JNIEnv *env; | |
| if (gdata->vmDead) { | |
| /* quietly ignore */ | |
| return JNI_TRUE; | |
| } | |
| requestCount = inStream_readInt(in); | |
| if (inStream_error(in)) { | |
| return JNI_TRUE; | |
| } | |
| env = getEnv(); | |
| for (i = 0; i < requestCount; i++) { | |
| id = inStream_readObjectID(in); | |
| refCount = inStream_readInt(in); | |
| if (inStream_error(in)) { | |
| return JNI_TRUE; | |
| } | |
| commonRef_releaseMultiple(env, id, refCount); | |
| } | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| holdEvents(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| eventHelper_holdEvents(); | |
| return JNI_TRUE; | |
| } | |
| static jboolean | |
| releaseEvents(PacketInputStream *in, PacketOutputStream *out) | |
| { | |
| eventHelper_releaseEvents(); | |
| return JNI_TRUE; | |
| } | |
| void *VirtualMachine_Cmds[] = { (void *)21 | |
| ,(void *)version | |
| ,(void *)classesForSignature | |
| ,(void *)allClasses | |
| ,(void *)getAllThreads | |
| ,(void *)topLevelThreadGroups | |
| ,(void *)dispose | |
| ,(void *)idSizes | |
| ,(void *)suspend | |
| ,(void *)resume | |
| ,(void *)doExit | |
| ,(void *)createString | |
| ,(void *)capabilities | |
| ,(void *)classPaths | |
| ,(void *)disposeObjects | |
| ,(void *)holdEvents | |
| ,(void *)releaseEvents | |
| ,(void *)capabilitiesNew | |
| ,(void *)redefineClasses | |
| ,(void *)setDefaultStratum | |
| ,(void *)allClassesWithGeneric | |
| ,(void *)instanceCounts | |
| }; |