| /* |
| * 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" |
| |
| // ANDROID-CHANGED: Need to sent metrics before doExit |
| #include "timing.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>"; |
| } |
| // ANDROID-CHANGED: The runtime value of the java.version property has always been "0" on |
| // android but the old debugger just sent a different value. Simply sending "0" |
| // can confuse some JDWP clients so we will simply say that we are version "8". |
| if (strcmp(gdata->property_java_vm_name, "Dalvik") == 0 && strcmp(vmVersion, "0") == 0) { |
| vmVersion = "8"; |
| } |
| 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) |
| { |
| // ANDROID-CHANGED: We are about to exit(). Send ART cmd processing time, |
| // if there are any remaining. |
| timings_flush(); |
| |
| 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; |
| } |
| |
| // ANDROID-CHANGED: We want to adjust the capabilities slightly if we are on android. |
| jboolean is_android_runtime = strcmp(gdata->property_java_vm_name, "Dalvik") == 0; |
| |
| (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 */ |
| /* ANDROID-CHANGED: some jdwp clients will send us class files for redefineClasses which we do |
| * not support. Set this capability to false and set reserved32 instead to indicate that we do |
| * support .dex file class redefinition. |
| */ |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_redefine_classes && !is_android_runtime); |
| (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 */ |
| /* ANDROID-CHANGED: Use the reserved32 capability to notify clients that we can support dex |
| * class redefinition. |
| */ |
| (void)outStream_writeBoolean(out, (jboolean)caps.can_redefine_classes && is_android_runtime); |
| 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 |
| }; |