/* * Copyright (c) 1998, 2022, 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.
*/
/* * For primitive types, the type key is bounced back as is.
*/
(void)outStream_writeByte(out, typeKey); switch (typeKey) { case JDWP_TAG(BYTE):
(void)outStream_writeByte(out,
JNI_FUNC_PTR(env,GetStaticByteField)(env, clazz, field)); break;
case JDWP_TAG(CHAR):
(void)outStream_writeChar(out,
JNI_FUNC_PTR(env,GetStaticCharField)(env, clazz, field)); break;
case JDWP_TAG(FLOAT):
(void)outStream_writeFloat(out,
JNI_FUNC_PTR(env,GetStaticFloatField)(env, clazz, field)); break;
case JDWP_TAG(DOUBLE):
(void)outStream_writeDouble(out,
JNI_FUNC_PTR(env,GetStaticDoubleField)(env, clazz, field)); break;
case JDWP_TAG(INT):
(void)outStream_writeInt(out,
JNI_FUNC_PTR(env,GetStaticIntField)(env, clazz, field)); break;
case JDWP_TAG(LONG):
(void)outStream_writeLong(out,
JNI_FUNC_PTR(env,GetStaticLongField)(env, clazz, field)); break;
case JDWP_TAG(SHORT):
(void)outStream_writeShort(out,
JNI_FUNC_PTR(env,GetStaticShortField)(env, clazz, field)); break;
case JDWP_TAG(BOOLEAN):
(void)outStream_writeBoolean(out,
JNI_FUNC_PTR(env,GetStaticBooleanField)(env, clazz, field)); break;
}
}
/* * Instance methods start with the instance, thread and class, * and statics and constructors start with the class and then the * thread.
*/
env = getEnv(); if (inStream_command(in) == JDWP_COMMAND(ObjectReference, InvokeMethod)) {
instance = inStream_readObjectRef(env, in);
thread = inStream_readThreadRef(env, in);
clazz = inStream_readClassRef(env, in);
} else { /* static method or constructor */
instance = NULL;
clazz = inStream_readClassRef(env, in);
thread = inStream_readThreadRef(env, in);
}
/* * ... and the rest of the packet is identical for all commands
*/
method = inStream_readMethodID(in);
argumentCount = inStream_readInt(in); if (inStream_error(in)) { return JNI_TRUE;
}
/* If count == 0, don't try and allocate 0 bytes, you'll get NULL */ if ( argumentCount > 0 ) { int i; /*LINTED*/
arguments = jvmtiAllocate(argumentCount * (jint)sizeof(*arguments)); if (arguments == NULL) {
outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY)); return JNI_TRUE;
} for (i = 0; (i < argumentCount) && !inStream_error(in); i++) {
arguments[i] = inStream_readValue(in);
} if (inStream_error(in)) { return JNI_TRUE;
}
}
options = inStream_readInt(in); if (inStream_error(in)) { if ( arguments != NULL ) {
jvmtiDeallocate(arguments);
} return JNI_TRUE;
}
/* * Request the invoke. If there are no errors in the request, * the interrupting thread will actually do the invoke and a * reply will be generated subsequently, so we don't reply here.
*/
error = invoker_requestInvoke(invokeType, (jbyte)options, inStream_id(in),
thread, clazz, method,
instance, arguments, argumentCount); if (error != JVMTI_ERROR_NONE) {
outStream_setError(out, map2jdwpError(error)); if ( arguments != NULL ) {
jvmtiDeallocate(arguments);
} return JNI_TRUE;
}
int
filterDebugThreads(jthread *threads, int count)
{ int i; int current;
/* Squish out all of the debugger-spawned threads */ for (i = 0, current = 0; i < count; i++) {
jthread thread = threads[i]; if (!threadControl_isDebugThread(thread)) { if (i > current) {
threads[current] = thread;
}
current++;
}
} return current;
}
jbyte
referenceTypeTag(jclass clazz)
{
jbyte tag;
if (isInterface(clazz)) {
tag = JDWP_TYPE_TAG(INTERFACE);
} elseif (isArrayClass(clazz)) {
tag = JDWP_TYPE_TAG(ARRAY);
} else {
tag = JDWP_TYPE_TAG(CLASS);
}
return tag;
}
/** * Get field modifiers
*/
jvmtiError
fieldModifiers(jclass clazz, jfieldID field, jint *pmodifiers)
{
jvmtiError error;
/* Returns the start and end locations of the specified method. */
jvmtiError
methodLocation(jmethodID method, jlocation *ploc1, jlocation *ploc2)
{
jvmtiError error;
/* * Get the return type key of the method * V or B C D F I J S Z L [
*/
jvmtiError
methodReturnType(jmethodID method, char *typeKey)
{ char *signature;
jvmtiError error;
/* * Make the debugger thread a daemon
*/
JNI_FUNC_PTR(env,CallVoidMethod)
(env, thread, gdata->threadSetDaemon, JNI_TRUE); if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
JNI_FUNC_PTR(env,ExceptionClear)(env);
error = AGENT_ERROR_JNI_EXCEPTION; goto err;
}
error = threadControl_addDebugThread(thread); if (error == JVMTI_ERROR_NONE) { /* * Debugger threads need cycles in all sorts of strange * situations (e.g. infinite cpu-bound loops), so give the * thread a high priority. Note that if the VM has an application * thread running at the max priority, there is still a chance * that debugger threads will be starved. (There needs to be * a way to give debugger threads a priority higher than any * application thread).
*/
error = JVMTI_FUNC_PTR(gdata->jvmti,RunAgentThread)
(gdata->jvmti, thread, func, arg,
JVMTI_THREAD_MAX_PRIORITY);
}
err: ;
} END_WITH_LOCAL_REFS(env);
return error;
}
jvmtiError
jvmtiGetCapabilities(jvmtiCapabilities *caps)
{ if ( gdata->vmDead ) { return AGENT_ERROR_VM_DEAD;
} if (!gdata->haveCachedJvmtiCapabilities) {
jvmtiError error;
staticvoid
handleInterrupt(void)
{ /* * An interrupt is handled: * * 1) for running application threads by deferring the interrupt * until the current event handler has concluded. * * 2) for debugger threads by ignoring the interrupt; this is the * most robust solution since debugger threads don't use interrupts * to signal any condition. * * 3) for application threads that have not started or already * ended by ignoring the interrupt. In the former case, the application * is relying on timing to determine whether or not the thread sees * the interrupt; in the latter case, the interrupt is meaningless.
*/
jthread thread = threadControl_currentThread(); if ((thread != NULL) && (!threadControl_isDebugThread(thread))) {
threadControl_setPendingInterrupt(thread);
}
}
static jvmtiError
ignore_vm_death(jvmtiError error)
{ if (error == JVMTI_ERROR_WRONG_PHASE) {
LOG_MISC(("VM_DEAD, in debugMonitor*()?")); return JVMTI_ERROR_NONE; /* JVMTI does this, not JVMDI? */
} return error;
}
/* * According to the JLS (17.8), here we have * either : * a- been notified * b- gotten a spurious wakeup * c- been interrupted * If both a and c have happened, the VM must choose * which way to return - a or c. If it chooses c * then the notify is gone - either to some other * thread that is also waiting, or it is dropped * on the floor. * * a is what we expect. b won't hurt us any - * callers should be programmed to handle * spurious wakeups. In case of c, * then the interrupt has been cleared, but * we don't want to consume it. It came from * user code and is intended for user code, not us. * So, we will remember that the interrupt has * occurred and re-activate it when this thread * goes back into user code. * That being said, what do we do here? Since * we could have been notified too, here we will * just pretend that we have been. It won't hurt * anything to return in the same way as if * we were notified since callers have to be able to * handle spurious wakeups anyway.
*/ if (error == JVMTI_ERROR_INTERRUPT) {
handleInterrupt();
error = JVMTI_ERROR_NONE;
}
error = ignore_vm_death(error); if (error != JVMTI_ERROR_NONE) {
EXIT_ERROR(error, "on raw monitor wait");
}
}
error = JVMTI_FUNC_PTR(gdata->jvmti,DestroyRawMonitor)
(gdata->jvmti, monitor);
error = ignore_vm_death(error); if (error != JVMTI_ERROR_NONE) {
EXIT_ERROR(error, "on destruction of raw monitor");
}
}
/** * Return array of all threads (must be inside a WITH_LOCAL_REFS)
*/
jthread *
allThreads(jint *count)
{
jthread *threads;
jvmtiError error;
*count = 0;
threads = NULL;
error = JVMTI_FUNC_PTR(gdata->jvmti,GetAllThreads)
(gdata->jvmti, count, &threads); if (error == AGENT_ERROR_OUT_OF_MEMORY) { return NULL; /* Let caller deal with no memory? */
} if (error != JVMTI_ERROR_NONE) {
EXIT_ERROR(error, "getting all threads");
} return threads;
}
/** * Fill the passed in structure with thread group info. * name field is JVMTI allocated. parent is global ref.
*/ void
threadGroupInfo(jthreadGroup group, jvmtiThreadGroupInfo *info)
{
jvmtiError error;
error = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadGroupInfo)
(gdata->jvmti, group, info); if (error != JVMTI_ERROR_NONE) {
EXIT_ERROR(error, "on getting thread group info");
}
}
error = JVMTI_FUNC_PTR(gdata->jvmti,IsFieldSynthetic)
(gdata->jvmti, clazz, field, psynthetic); if ( error == JVMTI_ERROR_MUST_POSSESS_CAPABILITY ) { /* If the query is not supported, we assume it is not synthetic. */
*psynthetic = JNI_FALSE; return JVMTI_ERROR_NONE;
} return error;
}
error = JVMTI_FUNC_PTR(gdata->jvmti,IsMethodSynthetic)
(gdata->jvmti, method, psynthetic); if ( error == JVMTI_ERROR_MUST_POSSESS_CAPABILITY ) { /* If the query is not supported, we assume it is not synthetic. */
*psynthetic = JNI_FALSE; return JVMTI_ERROR_NONE;
} return error;
}
/* Get all loaded classes for a loader (must be inside a WITH_LOCAL_REFS) */
jvmtiError
allClassLoaderClasses(jobject loader, jclass **ppclasses, jint *pcount)
{
jvmtiError error;
static jboolean
is_a_nested_class(char *outer_sig, int outer_sig_len, char *sig, int sep)
{ char *inner;
/* Assumed outer class signature is "LOUTERCLASSNAME;" * inner class signature is "LOUTERCLASSNAME$INNERNAME;" * * INNERNAME can take the form: * [0-9][1-9]* anonymous class somewhere in the file * [0-9][1-9]*NAME local class somewhere in the OUTER class * NAME nested class in OUTER * * If NAME itself contains a $ (sep) then classname is further nested * inside another class. *
*/
/* Check prefix first */ if ( strncmp(sig, outer_sig, outer_sig_len-1) != 0 ) { return JNI_FALSE;
}
/* Prefix must be followed by a $ (sep) */ if ( sig[outer_sig_len-1] != sep ) { return JNI_FALSE; /* No sep follows the match, must not be nested. */
}
/* Walk past any digits, if we reach the end, must be pure anonymous */
inner = sig + outer_sig_len; #if 1 /* We want to return local classes */ while ( *inner && isdigit(*inner) ) {
inner++;
} /* But anonymous class names can't be trusted. */ if ( *inner == ';' ) { return JNI_FALSE; /* A pure anonymous class */
} #else if ( *inner && isdigit(*inner) ) { return JNI_FALSE; /* A pure anonymous or local class */
} #endif
/* Nested deeper? */ if ( strchr(inner, sep) != NULL ) { return JNI_FALSE; /* Nested deeper than we want? */
} return JNI_TRUE;
}
/* Get all nested classes for a class (must be inside a WITH_LOCAL_REFS) */
jvmtiError
allNestedClasses(jclass parent_clazz, jclass **ppnested, jint *pcount)
{
jvmtiError error;
jobject parent_loader;
jclass *classes; char *signature;
size_t len;
jint count;
jint ncount; int i;
void
createLocalRefSpace(JNIEnv *env, jint capacity)
{ /* * Save current exception since it might get overwritten by * the calls below. Note we must depend on space in the existing * frame because asking for a new frame may generate an exception.
*/
jobject throwable = JNI_FUNC_PTR(env,ExceptionOccurred)(env);
/* * Use the current frame if necessary; otherwise create a new one
*/ if (JNI_FUNC_PTR(env,PushLocalFrame)(env, capacity) < 0) {
EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"PushLocalFrame: Unable to push JNI frame");
}
/* * TO DO: This could be more efficient if it used EnsureLocalCapacity, * but that would not work if two functions on the call stack * use this function. We would need to either track reserved * references on a per-thread basis or come up with a convention * that would prevent two functions from depending on this function * at the same time.
*/
/* * Restore exception state from before call
*/ if (throwable != NULL) {
JNI_FUNC_PTR(env,Throw)(env, throwable);
} else {
JNI_FUNC_PTR(env,ExceptionClear)(env);
}
}
/** * Return property value as jstring
*/ static jstring
getPropertyValue(JNIEnv *env, char *propertyName)
{
jstring valueString;
jstring nameString;
valueString = NULL;
/* Create new String object to hold the property name */
nameString = JNI_FUNC_PTR(env,NewStringUTF)(env, propertyName); if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
JNI_FUNC_PTR(env,ExceptionClear)(env); /* NULL will be returned below */
} else { /* Call valueString = System.getProperty(nameString) */
valueString = JNI_FUNC_PTR(env,CallStaticObjectMethod)
(env, gdata->systemClass, gdata->systemGetProperty, nameString); if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
JNI_FUNC_PTR(env,ExceptionClear)(env);
valueString = NULL;
}
} return valueString;
}
/** * Set an agent property
*/ void
setAgentPropertyValue(JNIEnv *env, char *propertyName, char* propertyValue)
{
jstring nameString;
jstring valueString;
if (gdata->agent_properties == NULL) { /* VMSupport doesn't exist; so ignore */ return;
}
/* Create jstrings for property name and value */
nameString = JNI_FUNC_PTR(env,NewStringUTF)(env, propertyName); if (nameString != NULL) { /* convert the value to UTF8 */ int len; char *utf8value; int utf8maxSize;
len = (int)strlen(propertyValue);
utf8maxSize = len * 4 + 1;
utf8value = (char *)jvmtiAllocate(utf8maxSize); if (utf8value != NULL) {
utf8FromPlatform(propertyValue, len, (jbyte *)utf8value, utf8maxSize);
valueString = JNI_FUNC_PTR(env, NewStringUTF)(env, utf8value);
jvmtiDeallocate(utf8value);
/** * Return property value as JDWP allocated string in UTF8 encoding
*/ staticchar *
getPropertyUTF8(JNIEnv *env, char *propertyName)
{
jvmtiError error; char *value;
value = NULL;
error = JVMTI_FUNC_PTR(gdata->jvmti,GetSystemProperty)
(gdata->jvmti, (constchar *)propertyName, &value); if (error != JVMTI_ERROR_NONE) {
jstring valueString;
value = NULL;
valueString = getPropertyValue(env, propertyName);
if (valueString != NULL) { constchar *utf;
/* Get the UTF8 encoding for this property value string */
utf = JNI_FUNC_PTR(env,GetStringUTFChars)(env, valueString, NULL); /* Make a copy for returning, release the JNI copy */
value = jvmtiAllocate((int)strlen(utf) + 1); if (value != NULL) {
(void)strcpy(value, utf);
}
JNI_FUNC_PTR(env,ReleaseStringUTFChars)(env, valueString, utf);
}
} if ( value == NULL ) {
ERROR_MESSAGE(("JDWP Can't get property value for %s", propertyName));
EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL);
} return value;
}
/* Rarely needed, transport library uses JDWP errors, only use? */
jvmtiError
map2jvmtiError(jdwpError error)
{ switch ( error ) { case JDWP_ERROR(NONE): return JVMTI_ERROR_NONE; case JDWP_ERROR(INVALID_THREAD): return JVMTI_ERROR_INVALID_THREAD; case JDWP_ERROR(INVALID_THREAD_GROUP): return JVMTI_ERROR_INVALID_THREAD_GROUP; case JDWP_ERROR(INVALID_PRIORITY): return JVMTI_ERROR_INVALID_PRIORITY; case JDWP_ERROR(THREAD_NOT_SUSPENDED): return JVMTI_ERROR_THREAD_NOT_SUSPENDED; case JDWP_ERROR(THREAD_SUSPENDED): return JVMTI_ERROR_THREAD_SUSPENDED; case JDWP_ERROR(INVALID_OBJECT): return JVMTI_ERROR_INVALID_OBJECT; case JDWP_ERROR(INVALID_CLASS): return JVMTI_ERROR_INVALID_CLASS; case JDWP_ERROR(CLASS_NOT_PREPARED): return JVMTI_ERROR_CLASS_NOT_PREPARED; case JDWP_ERROR(INVALID_METHODID): return JVMTI_ERROR_INVALID_METHODID; case JDWP_ERROR(INVALID_LOCATION): return JVMTI_ERROR_INVALID_LOCATION; case JDWP_ERROR(INVALID_FIELDID): return JVMTI_ERROR_INVALID_FIELDID; case JDWP_ERROR(INVALID_FRAMEID): return AGENT_ERROR_INVALID_FRAMEID; case JDWP_ERROR(NO_MORE_FRAMES): return JVMTI_ERROR_NO_MORE_FRAMES; case JDWP_ERROR(OPAQUE_FRAME): return JVMTI_ERROR_OPAQUE_FRAME; case JDWP_ERROR(NOT_CURRENT_FRAME): return AGENT_ERROR_NOT_CURRENT_FRAME; case JDWP_ERROR(TYPE_MISMATCH): return JVMTI_ERROR_TYPE_MISMATCH; case JDWP_ERROR(INVALID_SLOT): return JVMTI_ERROR_INVALID_SLOT; case JDWP_ERROR(DUPLICATE): return JVMTI_ERROR_DUPLICATE; case JDWP_ERROR(NOT_FOUND): return JVMTI_ERROR_NOT_FOUND; case JDWP_ERROR(INVALID_MONITOR): return JVMTI_ERROR_INVALID_MONITOR; case JDWP_ERROR(NOT_MONITOR_OWNER): return JVMTI_ERROR_NOT_MONITOR_OWNER; case JDWP_ERROR(INTERRUPT): return JVMTI_ERROR_INTERRUPT; case JDWP_ERROR(INVALID_CLASS_FORMAT): return JVMTI_ERROR_INVALID_CLASS_FORMAT; case JDWP_ERROR(CIRCULAR_CLASS_DEFINITION): return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION; case JDWP_ERROR(FAILS_VERIFICATION): return JVMTI_ERROR_FAILS_VERIFICATION; case JDWP_ERROR(ADD_METHOD_NOT_IMPLEMENTED): return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED; case JDWP_ERROR(SCHEMA_CHANGE_NOT_IMPLEMENTED): return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED; case JDWP_ERROR(INVALID_TYPESTATE): return JVMTI_ERROR_INVALID_TYPESTATE; case JDWP_ERROR(HIERARCHY_CHANGE_NOT_IMPLEMENTED): return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED; case JDWP_ERROR(DELETE_METHOD_NOT_IMPLEMENTED): return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED; case JDWP_ERROR(UNSUPPORTED_VERSION): return JVMTI_ERROR_UNSUPPORTED_VERSION; case JDWP_ERROR(NAMES_DONT_MATCH): return JVMTI_ERROR_NAMES_DONT_MATCH; case JDWP_ERROR(CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED): return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED; case JDWP_ERROR(METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED): return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED; case JDWP_ERROR(CLASS_ATTRIBUTE_CHANGE_NOT_IMPLEMENTED): return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED; case JDWP_ERROR(NOT_IMPLEMENTED): return JVMTI_ERROR_NOT_AVAILABLE; case JDWP_ERROR(NULL_POINTER): return JVMTI_ERROR_NULL_POINTER; case JDWP_ERROR(ABSENT_INFORMATION): return JVMTI_ERROR_ABSENT_INFORMATION; case JDWP_ERROR(INVALID_EVENT_TYPE): return JVMTI_ERROR_INVALID_EVENT_TYPE; case JDWP_ERROR(ILLEGAL_ARGUMENT): return JVMTI_ERROR_ILLEGAL_ARGUMENT; case JDWP_ERROR(OUT_OF_MEMORY): return JVMTI_ERROR_OUT_OF_MEMORY; case JDWP_ERROR(ACCESS_DENIED): return JVMTI_ERROR_ACCESS_DENIED; case JDWP_ERROR(VM_DEAD): return JVMTI_ERROR_WRONG_PHASE; case JDWP_ERROR(UNATTACHED_THREAD): return JVMTI_ERROR_UNATTACHED_THREAD; case JDWP_ERROR(INVALID_TAG): return AGENT_ERROR_INVALID_TAG; case JDWP_ERROR(ALREADY_INVOKING): return AGENT_ERROR_ALREADY_INVOKING; case JDWP_ERROR(INVALID_INDEX): return AGENT_ERROR_INVALID_INDEX; case JDWP_ERROR(INVALID_LENGTH): return AGENT_ERROR_INVALID_LENGTH; case JDWP_ERROR(INVALID_STRING): return AGENT_ERROR_INVALID_STRING; case JDWP_ERROR(INVALID_CLASS_LOADER): return AGENT_ERROR_INVALID_CLASS_LOADER; case JDWP_ERROR(INVALID_ARRAY): return AGENT_ERROR_INVALID_ARRAY; case JDWP_ERROR(TRANSPORT_LOAD): return AGENT_ERROR_TRANSPORT_LOAD; case JDWP_ERROR(TRANSPORT_INIT): return AGENT_ERROR_TRANSPORT_INIT; case JDWP_ERROR(NATIVE_METHOD): return AGENT_ERROR_NATIVE_METHOD; case JDWP_ERROR(INVALID_COUNT): return AGENT_ERROR_INVALID_COUNT; case JDWP_ERROR(INTERNAL): return AGENT_ERROR_JDWP_INTERNAL;
} return AGENT_ERROR_INTERNAL;
}
char*
eventIndex2EventName(EventIndex ei)
{ switch ( ei ) { case EI_SINGLE_STEP: return"EI_SINGLE_STEP"; case EI_BREAKPOINT: return"EI_BREAKPOINT"; case EI_FRAME_POP: return"EI_FRAME_POP"; case EI_EXCEPTION: return"EI_EXCEPTION"; case EI_THREAD_START: return"EI_THREAD_START"; case EI_THREAD_END: return"EI_THREAD_END"; case EI_CLASS_PREPARE: return"EI_CLASS_PREPARE"; case EI_CLASS_UNLOAD: return"EI_CLASS_UNLOAD"; case EI_CLASS_LOAD: return"EI_CLASS_LOAD"; case EI_FIELD_ACCESS: return"EI_FIELD_ACCESS"; case EI_FIELD_MODIFICATION: return"EI_FIELD_MODIFICATION"; case EI_EXCEPTION_CATCH: return"EI_EXCEPTION_CATCH"; case EI_METHOD_ENTRY: return"EI_METHOD_ENTRY"; case EI_METHOD_EXIT: return"EI_METHOD_EXIT"; case EI_MONITOR_CONTENDED_ENTER: return"EI_MONITOR_CONTENDED_ENTER"; case EI_MONITOR_CONTENDED_ENTERED: return"EI_MONITOR_CONTENDED_ENTERED"; case EI_MONITOR_WAIT: return"EI_MONITOR_WAIT"; case EI_MONITOR_WAITED: return"EI_MONITOR_WAITED"; case EI_VM_INIT: return"EI_VM_INIT"; case EI_VM_DEATH: return"EI_VM_DEATH"; case EI_VIRTUAL_THREAD_START: return"EI_VIRTUAL_THREAD_START"; case EI_VIRTUAL_THREAD_END: return"EI_VIRTUAL_THREAD_END"; default:
JDI_ASSERT(JNI_FALSE); return"Bad EI";
}
}
#endif
EventIndex
jdwp2EventIndex(jdwpEvent eventType)
{ switch ( eventType ) { case JDWP_EVENT(SINGLE_STEP): return EI_SINGLE_STEP; case JDWP_EVENT(BREAKPOINT): return EI_BREAKPOINT; case JDWP_EVENT(FRAME_POP): return EI_FRAME_POP; case JDWP_EVENT(EXCEPTION): return EI_EXCEPTION; case JDWP_EVENT(THREAD_START): return EI_THREAD_START; case JDWP_EVENT(THREAD_END): return EI_THREAD_END; case JDWP_EVENT(CLASS_PREPARE): return EI_CLASS_PREPARE; case JDWP_EVENT(CLASS_UNLOAD): return EI_CLASS_UNLOAD; case JDWP_EVENT(CLASS_LOAD): return EI_CLASS_LOAD; case JDWP_EVENT(FIELD_ACCESS): return EI_FIELD_ACCESS; case JDWP_EVENT(FIELD_MODIFICATION): return EI_FIELD_MODIFICATION; case JDWP_EVENT(EXCEPTION_CATCH): return EI_EXCEPTION_CATCH; case JDWP_EVENT(METHOD_ENTRY): return EI_METHOD_ENTRY; case JDWP_EVENT(METHOD_EXIT): return EI_METHOD_EXIT; case JDWP_EVENT(METHOD_EXIT_WITH_RETURN_VALUE): return EI_METHOD_EXIT; case JDWP_EVENT(MONITOR_CONTENDED_ENTER): return EI_MONITOR_CONTENDED_ENTER; case JDWP_EVENT(MONITOR_CONTENDED_ENTERED): return EI_MONITOR_CONTENDED_ENTERED; case JDWP_EVENT(MONITOR_WAIT): return EI_MONITOR_WAIT; case JDWP_EVENT(MONITOR_WAITED): return EI_MONITOR_WAITED; case JDWP_EVENT(VM_INIT): return EI_VM_INIT; case JDWP_EVENT(VM_DEATH): return EI_VM_DEATH; default: break;
}
/* * Event type not recognized - don't exit with error as caller * may wish to return error to debugger.
*/ return (EventIndex)0;
}
EventIndex
jvmti2EventIndex(jvmtiEvent kind)
{ switch ( kind ) { case JVMTI_EVENT_SINGLE_STEP: return EI_SINGLE_STEP; case JVMTI_EVENT_BREAKPOINT: return EI_BREAKPOINT; case JVMTI_EVENT_FRAME_POP: return EI_FRAME_POP; case JVMTI_EVENT_EXCEPTION: return EI_EXCEPTION; case JVMTI_EVENT_THREAD_START: return EI_THREAD_START; case JVMTI_EVENT_THREAD_END: return EI_THREAD_END; case JVMTI_EVENT_CLASS_PREPARE: return EI_CLASS_PREPARE; case JVMTI_EVENT_CLASS_LOAD: return EI_CLASS_LOAD; case JVMTI_EVENT_FIELD_ACCESS: return EI_FIELD_ACCESS; case JVMTI_EVENT_FIELD_MODIFICATION: return EI_FIELD_MODIFICATION; case JVMTI_EVENT_EXCEPTION_CATCH: return EI_EXCEPTION_CATCH; case JVMTI_EVENT_METHOD_ENTRY: return EI_METHOD_ENTRY; case JVMTI_EVENT_METHOD_EXIT: return EI_METHOD_EXIT; /* * There is no JVMTI_EVENT_METHOD_EXIT_WITH_RETURN_VALUE. * The normal JVMTI_EVENT_METHOD_EXIT always contains the return value.
*/ case JVMTI_EVENT_MONITOR_CONTENDED_ENTER: return EI_MONITOR_CONTENDED_ENTER; case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED: return EI_MONITOR_CONTENDED_ENTERED; case JVMTI_EVENT_MONITOR_WAIT: return EI_MONITOR_WAIT; case JVMTI_EVENT_MONITOR_WAITED: return EI_MONITOR_WAITED; case JVMTI_EVENT_VM_INIT: return EI_VM_INIT; case JVMTI_EVENT_VM_DEATH: return EI_VM_DEATH; /* vthread events */ case JVMTI_EVENT_VIRTUAL_THREAD_START: return EI_VIRTUAL_THREAD_START; case JVMTI_EVENT_VIRTUAL_THREAD_END: return EI_VIRTUAL_THREAD_END;
default:
EXIT_ERROR(AGENT_ERROR_INVALID_INDEX,"JVMTI to EventIndex mapping"); break;
} return (EventIndex)0;
}
/* This routine is commonly used, maps jvmti and agent errors to the best * jdwp error code we can map to.
*/
jdwpError
map2jdwpError(jvmtiError error)
{ switch ( (int)error ) { case JVMTI_ERROR_NONE: return JDWP_ERROR(NONE); case AGENT_ERROR_INVALID_THREAD: case JVMTI_ERROR_INVALID_THREAD: return JDWP_ERROR(INVALID_THREAD); case JVMTI_ERROR_INVALID_THREAD_GROUP: return JDWP_ERROR(INVALID_THREAD_GROUP); case JVMTI_ERROR_INVALID_PRIORITY: return JDWP_ERROR(INVALID_PRIORITY); case JVMTI_ERROR_THREAD_NOT_SUSPENDED: return JDWP_ERROR(THREAD_NOT_SUSPENDED); case JVMTI_ERROR_THREAD_SUSPENDED: return JDWP_ERROR(THREAD_SUSPENDED); case JVMTI_ERROR_THREAD_NOT_ALIVE: return JDWP_ERROR(INVALID_THREAD); case AGENT_ERROR_INVALID_OBJECT: case JVMTI_ERROR_INVALID_OBJECT: return JDWP_ERROR(INVALID_OBJECT); case JVMTI_ERROR_INVALID_CLASS: return JDWP_ERROR(INVALID_CLASS); case JVMTI_ERROR_CLASS_NOT_PREPARED: return JDWP_ERROR(CLASS_NOT_PREPARED); case JVMTI_ERROR_INVALID_METHODID: return JDWP_ERROR(INVALID_METHODID); case JVMTI_ERROR_INVALID_LOCATION: return JDWP_ERROR(INVALID_LOCATION); case JVMTI_ERROR_INVALID_FIELDID: return JDWP_ERROR(INVALID_FIELDID); case AGENT_ERROR_NO_MORE_FRAMES: case JVMTI_ERROR_NO_MORE_FRAMES: return JDWP_ERROR(NO_MORE_FRAMES); case JVMTI_ERROR_OPAQUE_FRAME: return JDWP_ERROR(OPAQUE_FRAME); case JVMTI_ERROR_TYPE_MISMATCH: return JDWP_ERROR(TYPE_MISMATCH); case JVMTI_ERROR_INVALID_SLOT: return JDWP_ERROR(INVALID_SLOT); case JVMTI_ERROR_DUPLICATE: return JDWP_ERROR(DUPLICATE); case JVMTI_ERROR_NOT_FOUND: return JDWP_ERROR(NOT_FOUND); case JVMTI_ERROR_INVALID_MONITOR: return JDWP_ERROR(INVALID_MONITOR); case JVMTI_ERROR_NOT_MONITOR_OWNER: return JDWP_ERROR(NOT_MONITOR_OWNER); case JVMTI_ERROR_INTERRUPT: return JDWP_ERROR(INTERRUPT); case JVMTI_ERROR_INVALID_CLASS_FORMAT: return JDWP_ERROR(INVALID_CLASS_FORMAT); case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION: return JDWP_ERROR(CIRCULAR_CLASS_DEFINITION); case JVMTI_ERROR_FAILS_VERIFICATION: return JDWP_ERROR(FAILS_VERIFICATION); case JVMTI_ERROR_INVALID_TYPESTATE: return JDWP_ERROR(INVALID_TYPESTATE); case JVMTI_ERROR_UNSUPPORTED_VERSION: return JDWP_ERROR(UNSUPPORTED_VERSION); case JVMTI_ERROR_NAMES_DONT_MATCH: return JDWP_ERROR(NAMES_DONT_MATCH); case AGENT_ERROR_NULL_POINTER: case JVMTI_ERROR_NULL_POINTER: return JDWP_ERROR(NULL_POINTER); case JVMTI_ERROR_ABSENT_INFORMATION: return JDWP_ERROR(ABSENT_INFORMATION); case AGENT_ERROR_INVALID_EVENT_TYPE: case JVMTI_ERROR_INVALID_EVENT_TYPE: return JDWP_ERROR(INVALID_EVENT_TYPE); case AGENT_ERROR_ILLEGAL_ARGUMENT: case JVMTI_ERROR_ILLEGAL_ARGUMENT: return JDWP_ERROR(ILLEGAL_ARGUMENT); case JVMTI_ERROR_OUT_OF_MEMORY: case AGENT_ERROR_OUT_OF_MEMORY: return JDWP_ERROR(OUT_OF_MEMORY); case JVMTI_ERROR_ACCESS_DENIED: return JDWP_ERROR(ACCESS_DENIED); case JVMTI_ERROR_WRONG_PHASE: case AGENT_ERROR_VM_DEAD: case AGENT_ERROR_NO_JNI_ENV: return JDWP_ERROR(VM_DEAD); case AGENT_ERROR_JNI_EXCEPTION: case JVMTI_ERROR_UNATTACHED_THREAD: return JDWP_ERROR(UNATTACHED_THREAD); case JVMTI_ERROR_NOT_AVAILABLE: case JVMTI_ERROR_MUST_POSSESS_CAPABILITY: return JDWP_ERROR(NOT_IMPLEMENTED); case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED: return JDWP_ERROR(HIERARCHY_CHANGE_NOT_IMPLEMENTED); case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED: return JDWP_ERROR(DELETE_METHOD_NOT_IMPLEMENTED); case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED: return JDWP_ERROR(ADD_METHOD_NOT_IMPLEMENTED); case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED: return JDWP_ERROR(SCHEMA_CHANGE_NOT_IMPLEMENTED); case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED: return JDWP_ERROR(CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED); case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED: return JDWP_ERROR(METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED); case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED: return JDWP_ERROR(CLASS_ATTRIBUTE_CHANGE_NOT_IMPLEMENTED); case JVMTI_ERROR_UNSUPPORTED_OPERATION: return JDWP_ERROR(NOT_IMPLEMENTED); case AGENT_ERROR_NOT_CURRENT_FRAME: return JDWP_ERROR(NOT_CURRENT_FRAME); case AGENT_ERROR_INVALID_TAG: return JDWP_ERROR(INVALID_TAG); case AGENT_ERROR_ALREADY_INVOKING: return JDWP_ERROR(ALREADY_INVOKING); case AGENT_ERROR_INVALID_INDEX: return JDWP_ERROR(INVALID_INDEX); case AGENT_ERROR_INVALID_LENGTH: return JDWP_ERROR(INVALID_LENGTH); case AGENT_ERROR_INVALID_STRING: return JDWP_ERROR(INVALID_STRING); case AGENT_ERROR_INVALID_CLASS_LOADER: return JDWP_ERROR(INVALID_CLASS_LOADER); case AGENT_ERROR_INVALID_ARRAY: return JDWP_ERROR(INVALID_ARRAY); case AGENT_ERROR_TRANSPORT_LOAD: return JDWP_ERROR(TRANSPORT_LOAD); case AGENT_ERROR_TRANSPORT_INIT: return JDWP_ERROR(TRANSPORT_INIT); case AGENT_ERROR_NATIVE_METHOD: return JDWP_ERROR(NATIVE_METHOD); case AGENT_ERROR_INVALID_COUNT: return JDWP_ERROR(INVALID_COUNT); case AGENT_ERROR_INVALID_FRAMEID: return JDWP_ERROR(INVALID_FRAMEID); case JVMTI_ERROR_INTERNAL: case JVMTI_ERROR_INVALID_ENVIRONMENT: case AGENT_ERROR_INTERNAL: case AGENT_ERROR_JVMTI_INTERNAL: case AGENT_ERROR_JDWP_INTERNAL: return JDWP_ERROR(INTERNAL); default: break;
} return JDWP_ERROR(INTERNAL);
}
jint
map2jdwpSuspendStatus(jint state)
{
jint status = 0; if ( ( state & JVMTI_THREAD_STATE_SUSPENDED ) != 0 ) {
status = JDWP_SUSPEND_STATUS(SUSPENDED);
} return status;
}
if ( ! ( state & JVMTI_THREAD_STATE_ALIVE ) ) { if ( state & JVMTI_THREAD_STATE_TERMINATED ) {
status = JDWP_THREAD_STATUS(ZOMBIE);
} else { /* FIXUP? New JDWP #define for not started? */
status = (jdwpThreadStatus)(-1);
}
} else { if ( state & JVMTI_THREAD_STATE_SLEEPING ) {
status = JDWP_THREAD_STATUS(SLEEPING);
} elseif ( state & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER ) {
status = JDWP_THREAD_STATUS(MONITOR);
} elseif ( state & JVMTI_THREAD_STATE_WAITING ) {
status = JDWP_THREAD_STATUS(WAIT);
} elseif ( state & JVMTI_THREAD_STATE_RUNNABLE ) {
status = JDWP_THREAD_STATUS(RUNNING);
}
} return status;
}
jint
map2jdwpClassStatus(jint classStatus)
{
jint status = 0; if ( ( classStatus & JVMTI_CLASS_STATUS_VERIFIED ) != 0 ) {
status |= JDWP_CLASS_STATUS(VERIFIED);
} if ( ( classStatus & JVMTI_CLASS_STATUS_PREPARED ) != 0 ) {
status |= JDWP_CLASS_STATUS(PREPARED);
} if ( ( classStatus & JVMTI_CLASS_STATUS_INITIALIZED ) != 0 ) {
status |= JDWP_CLASS_STATUS(INITIALIZED);
} if ( ( classStatus & JVMTI_CLASS_STATUS_ERROR ) != 0 ) {
status |= JDWP_CLASS_STATUS(ERROR);
} return status;
}
/* Check data structure */
data = (ClassInstancesData*)user_data; if (data == NULL) { return JVMTI_VISIT_ABORT;
}
/* If we have tagged enough objects, just abort */ if ( data->maxInstances != 0 && data->instCount >= data->maxInstances ) { return JVMTI_VISIT_ABORT;
}
/* If tagged already, just continue */ if ( (*tag_ptr) != (jlong)0 ) { return JVMTI_VISIT_OBJECTS;
}
/* Tag the object so we don't count it again, and so we can retrieve it */
(*tag_ptr) = data->objTag;
data->instCount++; return JVMTI_VISIT_OBJECTS;
}
/* Get instances for one class */
jvmtiError
classInstances(jclass klass, ObjectBatch *instances, int maxInstances)
{
ClassInstancesData data;
jvmtiHeapCallbacks heap_callbacks;
jvmtiError error;
jvmtiEnv *jvmti;
/* Check interface assumptions */
if (klass == NULL) { return AGENT_ERROR_INVALID_OBJECT;
}
/* Get jvmti environment to use */
jvmti = getSpecialJvmti(); if ( jvmti == NULL ) { return AGENT_ERROR_INTERNAL;
}
/* Setup data to passed around the callbacks */
data.instCount = 0;
data.maxInstances = maxInstances;
data.objTag = (jlong)1;
data.error = JVMTI_ERROR_NONE;
/* Clear out callbacks structure */
(void)memset(&heap_callbacks,0,sizeof(heap_callbacks));
/* Set the callbacks we want */
heap_callbacks.heap_reference_callback = &cbObjectTagInstance;
/* Follow references, no initiating object, just this class, all objects */
error = JVMTI_FUNC_PTR(jvmti,FollowReferences)
(jvmti, 0, klass, NULL, &heap_callbacks, &data); if ( error == JVMTI_ERROR_NONE ) {
error = data.error;
}
/* Get all the instances now that they are tagged */ if ( error == JVMTI_ERROR_NONE ) {
error = JVMTI_FUNC_PTR(jvmti,GetObjectsWithTags)
(jvmti, 1, &(data.objTag), &(instances->count),
&(instances->objects), NULL); /* Verify we got the count we expected */ if ( data.instCount != instances->count ) {
error = AGENT_ERROR_INTERNAL;
}
}
/* Dispose of any special jvmti environment */
(void)JVMTI_FUNC_PTR(jvmti,DisposeEnvironment)(jvmti); return error;
}
/* Macros to convert a class or instance tag to an index and back again */ #define INDEX2CLASSTAG(i) ((jlong)((i)+1)) #define CLASSTAG2INDEX(t) (((int)(t))-1) #define JLONG_ABS(x) (((x)<(jlong)0)?-(x):(x))
/* Structure to hold class count heap traversal data (arg user_data) */ typedefstruct ClassCountData { int classCount;
jlong *counts;
jlong negObjTag;
jvmtiError error;
} ClassCountData;
/* Two different cbObjectCounter's, one for FollowReferences, one for * IterateThroughHeap. Pick a card, any card.
*/
/* Check data structure */
data = (ClassCountData*)user_data; if (data == NULL) { return JVMTI_VISIT_ABORT;
}
/* Classes with no class_tag should have been filtered out. */ if ( class_tag == (jlong)0 ) {
data->error = AGENT_ERROR_INTERNAL; return JVMTI_VISIT_ABORT;
}
/* Class tag not one we really want (jclass not in supplied list) */ if ( class_tag == data->negObjTag ) { return JVMTI_VISIT_OBJECTS;
}
/* If object tag is negative, just continue, we counted it */
tag = (*tag_ptr); if ( tag < (jlong)0 ) { return JVMTI_VISIT_OBJECTS;
}
/* Tag the object with a negative value just so we don't count it again */ if ( tag == (jlong)0 ) { /* This object had no tag value, so we give it the negObjTag value */
(*tag_ptr) = data->negObjTag;
} else { /* If this object had a positive tag value, it must be one of the * jclass objects we tagged. We need to preserve the value of * this tag for later objects that might have this as a class * tag, so we just make the existing tag value negative.
*/
(*tag_ptr) = -tag;
}
/* Absolute value of class tag is an index into the counts[] array */
jindex = JLONG_ABS(class_tag);
index = CLASSTAG2INDEX(jindex); if (index < 0 || index >= data->classCount) {
data->error = AGENT_ERROR_ILLEGAL_ARGUMENT; return JVMTI_VISIT_ABORT;
}
/* Bump instance count on this class */
data->counts[index]++; return JVMTI_VISIT_OBJECTS;
}
/* Check data structure */
data = (ClassCountData*)user_data; if (data == NULL) { return JVMTI_VISIT_ABORT;
}
/* Classes with no tag should be filtered out. */ if ( class_tag == (jlong)0 ) {
data->error = AGENT_ERROR_INTERNAL; return JVMTI_VISIT_ABORT;
}
/* Class tag is actually an index into data arrays */
index = CLASSTAG2INDEX(class_tag); if (index < 0 || index >= data->classCount) {
data->error = AGENT_ERROR_ILLEGAL_ARGUMENT; return JVMTI_VISIT_ABORT;
}
/* Bump instance count on this class */
data->counts[index]++; return JVMTI_VISIT_OBJECTS;
}
/* Get instance counts for a set of classes */
jvmtiError
classInstanceCounts(jint classCount, jclass *classes, jlong *counts)
{
jvmtiHeapCallbacks heap_callbacks;
ClassCountData data;
jvmtiError error;
jvmtiEnv *jvmti; int i;
/* Initialize return information */ for ( i = 0 ; i < classCount ; i++ ) {
counts[i] = (jlong)0;
}
/* Get jvmti environment to use */
jvmti = getSpecialJvmti(); if ( jvmti == NULL ) { return AGENT_ERROR_INTERNAL;
}
/* Setup class data structure */
data.error = JVMTI_ERROR_NONE;
data.classCount = classCount;
data.counts = counts;
error = JVMTI_ERROR_NONE; /* Set tags on classes, use index in classes[] as the tag value. */
error = JVMTI_ERROR_NONE; for ( i = 0 ; i < classCount ; i++ ) { if (classes[i] != NULL) {
jlong tag;
tag = INDEX2CLASSTAG(i);
error = JVMTI_FUNC_PTR(jvmti,SetTag) (jvmti, classes[i], tag); if ( error != JVMTI_ERROR_NONE ) { break;
}
}
}
/* Traverse heap, two ways to do this for instance counts. */ if ( error == JVMTI_ERROR_NONE ) {
/* Clear out callbacks structure */
(void)memset(&heap_callbacks,0,sizeof(heap_callbacks));
/* Check debug flags to see how to do this. */ if ( (gdata->debugflags & USE_ITERATE_THROUGH_HEAP) == 0 ) {
/* Using FollowReferences only gives us live objects, but we * need to tag the objects to avoid counting them twice since * the callback is per reference. * The jclass objects have been tagged with their index in the * supplied list, and that tag may flip to negative if it * is also an object of interest. * All other objects being counted that weren't in the * supplied classes list will have a negative classCount * tag value. So all objects counted will have negative tags. * If the absolute tag value is an index in the supplied * list, then it's one of the supplied classes.
*/
data.negObjTag = -INDEX2CLASSTAG(classCount);
/* Setup callbacks, only using object reference callback */
heap_callbacks.heap_reference_callback = &cbObjectCounterFromRef;
/* Follow references, no initiating object, tagged classes only */
error = JVMTI_FUNC_PTR(jvmti,FollowReferences)
(jvmti, JVMTI_HEAP_FILTER_CLASS_UNTAGGED,
NULL, NULL, &heap_callbacks, &data);
} else {
/* Using IterateThroughHeap means that we will visit each object * once, so no special tag tricks here. Just simple counting. * However in this case the object might not be live, so we do * a GC beforehand to make sure we minimize this.
*/
/* FIXUP: Need some kind of trigger here to avoid excessive GC's? */
error = JVMTI_FUNC_PTR(jvmti,ForceGarbageCollection)(jvmti); if ( error != JVMTI_ERROR_NONE ) {
/* Setup callbacks, just need object callback */
heap_callbacks.heap_iteration_callback = &cbObjectCounter;
/* Iterate through entire heap, tagged classes only */
error = JVMTI_FUNC_PTR(jvmti,IterateThroughHeap)
(jvmti, JVMTI_HEAP_FILTER_CLASS_UNTAGGED,
NULL, &heap_callbacks, &data);
}
}
/* Use data error if needed */ if ( error == JVMTI_ERROR_NONE ) {
error = data.error;
}
}
/* Dispose of any special jvmti environment */
(void)JVMTI_FUNC_PTR(jvmti,DisposeEnvironment)(jvmti); return error;
}
/* Check data structure */
data = (ReferrerData*)user_data; if (data == NULL) { return JVMTI_VISIT_ABORT;
}
/* If we have tagged enough objects, just abort */ if ( data->maxObjects != 0 && data->refCount >= data->maxObjects ) { return JVMTI_VISIT_ABORT;
}
/* If not of interest, just continue */ if ( (*tag_ptr) != data->objTag ) { return JVMTI_VISIT_OBJECTS;
}
/* Self reference that we haven't counted? */ if ( tag_ptr == referrer_tag_ptr ) { if ( data->selfRef == JNI_FALSE ) {
data->selfRef = JNI_TRUE;
data->refCount++;
} return JVMTI_VISIT_OBJECTS;
}
/* If the referrer can be tagged, and hasn't been tagged, tag it */ if ( referrer_tag_ptr != NULL ) { if ( (*referrer_tag_ptr) == (jlong)0 ) {
*referrer_tag_ptr = data->refTag;
data->refCount++;
}
} return JVMTI_VISIT_OBJECTS;
}
/* Heap traversal to find referrers of an object */
jvmtiError
objectReferrers(jobject obj, ObjectBatch *referrers, int maxObjects)
{
jvmtiHeapCallbacks heap_callbacks;
ReferrerData data;
jvmtiError error;
jvmtiEnv *jvmti;
/* Get jvmti environment to use */
jvmti = getSpecialJvmti(); if ( jvmti == NULL ) { return AGENT_ERROR_INTERNAL;
}
/* Fill in the data structure passed around the callbacks */
data.refCount = 0;
data.maxObjects = maxObjects;
data.objTag = (jlong)1;
data.refTag = (jlong)2;
data.selfRef = JNI_FALSE;
data.error = JVMTI_ERROR_NONE;
/* Tag the object of interest */
error = JVMTI_FUNC_PTR(jvmti,SetTag) (jvmti, obj, data.objTag);
/* No need to go any further if we can't tag the object */ if ( error == JVMTI_ERROR_NONE ) {
/* Clear out callbacks structure */
(void)memset(&heap_callbacks,0,sizeof(heap_callbacks));
/* Setup callbacks we want */
heap_callbacks.heap_reference_callback = &cbObjectTagReferrer;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.