blob: 1e8b96938d39a645551ef621e81ece6196aed072 [file] [log] [blame]
/*
* 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
};