916 lines
26 KiB
C
916 lines
26 KiB
C
/*
|
|
* Copyright 1998-2006 Sun Microsystems, Inc. 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. Sun designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
* CA 95054 USA or visit www.sun.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 = 6; /* 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
|
|
};
|