/* * Given an object ID obtained from the debugger front end, return a * strong, global reference to that object (or NULL if the object * has been collected). The reference can then be used for JNI and * JVMTI calls. Caller is resposible for deleting the returned reference. */ jobject commonRef_idToRef(JNIEnv *env, jlong id) { jobject ref; ref = NULL; debugMonitorEnter(gdata->refLock); { RefNode *node; node = findNodeByID(env, id); if (node != NULL) { if (node->isStrong) { saveGlobalRef(env, node->ref, &ref); } else { jobject lref; lref = JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref); if ( lref == NULL ) { /* Object was GC'd shortly after we found the node */ deleteNodeByID(env, node->seqNum, ALL_REFS); } else { saveGlobalRef(env, node->ref, &ref); JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref); } } } } debugMonitorExit(gdata->refLock); return ref; }
static RefNode * createNode(JNIEnv *env, jobject ref) { RefNode *node; jobject weakRef; weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref); if (weakRef == NULL) { return NULL; } /* * TO DO: Consider fewer allocations of bigger chunks if * performance is a problem here. */ node = jvmtiAllocate(sizeof(*node)); if (node == NULL) { JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef); return NULL; } node->ref = weakRef; node->isStrong = JNI_FALSE; node->count = 1; node->seqNum = newSeqNum(); return node; }
/* Change a RefNode to have a strong reference */ static jobject strengthenNode(JNIEnv *env, RefNode *node) { if (!node->isStrong) { jobject strongRef; strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref); /* * NewGlobalRef on a weak ref will return NULL if the weak * reference has been collected or if out of memory. * We need to distinguish those two occurrences. */ if ((strongRef == NULL) && !isSameObject(env, node->ref, NULL)) { EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef"); } if (strongRef != NULL) { JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref); node->ref = strongRef; node->isStrong = JNI_TRUE; } return strongRef; } else { return node->ref; } }
static void deleteNode(JNIEnv *env, RefNode *node) { LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref)); if (node->isStrong) { JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref); } else { JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref); } jvmtiDeallocate(node); }
static jweak weakenNode(JNIEnv *env, RefNode *node) { if (node->isStrong) { jweak weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref); if (weakRef != NULL) { JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref); node->ref = weakRef; node->isStrong = JNI_FALSE; } return weakRef; } else { return node->ref; } }
WITH_LOCAL_REFS(env, length) { int i; jobject component; for (i = 0; i < length; i++) { component = JNI_FUNC_PTR(env,GetObjectArrayElement)(env, array, index + i); if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) { /* cleared by caller */ break; } (void)outStream_writeByte(out, specificTypeKey(env, component)); (void)outStream_writeObjectRef(env, out, component); } } END_WITH_LOCAL_REFS(env);
/* Delete a RefNode allocation, delete weak/global ref and clear tag */ static void deleteNode(JNIEnv *env, RefNode *node) { LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref)); if ( node->ref != NULL ) { /* Clear tag */ (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag) (gdata->jvmti, node->ref, NULL_OBJECT_ID); if (node->isStrong) { JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref); } else { JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref); } } gdata->objectsByIDcount--; jvmtiDeallocate(node); }
/* Create a fresh RefNode structure, create a weak ref and tag the object */ static RefNode * createNode(JNIEnv *env, jobject ref) { RefNode *node; jobject weakRef; jvmtiError error; /* Could allocate RefNode's in blocks, not sure it would help much */ node = (RefNode*)jvmtiAllocate((int)sizeof(RefNode)); if (node == NULL) { return NULL; } /* Create weak reference to make sure we have a reference */ weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref); if (weakRef == NULL) { jvmtiDeallocate(node); return NULL; } /* Set tag on weakRef */ error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag) (gdata->jvmti, weakRef, ptr_to_jlong(node)); if ( error != JVMTI_ERROR_NONE ) { JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef); jvmtiDeallocate(node); return NULL; } /* Fill in RefNode */ node->ref = weakRef; node->isStrong = JNI_FALSE; node->count = 1; node->seqNum = newSeqNum(); /* Count RefNode's created */ gdata->objectsByIDcount++; return node; }
static jboolean length(PacketInputStream *in, PacketOutputStream *out) { JNIEnv *env = getEnv(); jsize arrayLength; jarray array = inStream_readArrayRef(env, in); if (inStream_error(in)) { return JNI_TRUE; } arrayLength = JNI_FUNC_PTR(env,GetArrayLength)(env, array); (void)outStream_writeInt(out, arrayLength); return JNI_TRUE; }
static void writeByteComponents(JNIEnv *env, PacketOutputStream *out, jarray array, jint index, jint length) { jbyte *components; components = newComponents(out, length, sizeof(jbyte)); if (components != NULL) { jint i; JNI_FUNC_PTR(env,GetByteArrayRegion)(env, array, index, length, components); for (i = 0; i < length; i++) { (void)outStream_writeByte(out, components[i]); } deleteComponents(components); } }
/* * loadTransport() is adapted from loadJVMHelperLib() in * JDK 1.2 javai.c v1.61 */ static jdwpError loadTransport(char *name, jdwpTransportEnv **transportPtr) { JNIEnv *env; jdwpTransport_OnLoad_t onLoad; void *handle; char *libdir; /* Make sure library name is not empty */ if (name == NULL) { ERROR_MESSAGE(("library name is empty")); return JDWP_ERROR(TRANSPORT_LOAD); } /* First, look in sun.boot.library.path. This should find the standard * dt_socket and dt_shmem transport libraries, or any library * that was delivered with the J2SE. * Note: Since 6819213 fixed, Java property sun.boot.library.path can * contain multiple paths. Dll_dir is the first entry and * -Dsun.boot.library.path entries are appended. */ libdir = gdata->property_sun_boot_library_path; if (libdir == NULL) { ERROR_MESSAGE(("Java property sun.boot.library.path is not set")); return JDWP_ERROR(TRANSPORT_LOAD); } handle = loadTransportLibrary(libdir, name); if (handle == NULL) { /* Second, look along the path used by the native dlopen/LoadLibrary * functions. This should effectively try and load the simple * library name, which will cause the default system library * search technique to happen. * We should only reach here if the transport library wasn't found * in the J2SE directory, e.g. it's a custom transport library * not installed in the J2SE like dt_socket and dt_shmem is. * * Note: Why not use java.library.path? Several reasons: * a) This matches existing agentlib search * b) These are technically not JNI libraries */ handle = loadTransportLibrary("", name); } /* See if a library was found with this name */ if (handle == NULL) { ERROR_MESSAGE(("transport library not found: %s", name)); return JDWP_ERROR(TRANSPORT_LOAD); } /* Find the onLoad address */ onLoad = findTransportOnLoad(handle); if (onLoad == NULL) { ERROR_MESSAGE(("transport library missing onLoad entry: %s", name)); return JDWP_ERROR(TRANSPORT_LOAD); } /* Get transport interface */ env = getEnv(); if ( env != NULL ) { jdwpTransportEnv *t; JavaVM *jvm; jint ver; JNI_FUNC_PTR(env,GetJavaVM)(env, &jvm); ver = (*onLoad)(jvm, &callback, JDWPTRANSPORT_VERSION_1_0, &t); if (ver != JNI_OK) { switch (ver) { case JNI_ENOMEM : ERROR_MESSAGE(("insufficient memory to complete initialization")); break; case JNI_EVERSION : ERROR_MESSAGE(("transport doesn't recognize version %x", JDWPTRANSPORT_VERSION_1_0)); break; case JNI_EEXIST : ERROR_MESSAGE(("transport doesn't support multiple environments")); break; default: ERROR_MESSAGE(("unrecognized error %d from transport", ver)); break; } return JDWP_ERROR(TRANSPORT_INIT); } *transportPtr = t; } else { return JDWP_ERROR(TRANSPORT_LOAD); } return JDWP_ERROR(NONE); }
static void JNICALL cbEarlyException(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread, jmethodID method, jlocation location, jobject exception, jmethodID catch_method, jlocation catch_location) { jvmtiError error; jthrowable currentException; LOG_CB(("cbEarlyException: thread=%p", thread)); if ( gdata->vmDead ) { EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM dead at initial Exception event"); } if (!vmInitialized) { LOG_MISC(("VM is not initialized yet")); return; } /* * We want to preserve any current exception that might get wiped * out during event handling (e.g. JNI calls). We have to rely on * space for the local reference on the current frame because * doing a PushLocalFrame here might itself generate an exception. */ currentException = JNI_FUNC_PTR(env,ExceptionOccurred)(env); JNI_FUNC_PTR(env,ExceptionClear)(env); if (initOnUncaught && catch_method == NULL) { LOG_MISC(("Initializing on uncaught exception")); initialize(env, thread, EI_EXCEPTION); } else if (initOnException != NULL) { jclass clazz; /* Get class of exception thrown */ clazz = JNI_FUNC_PTR(env,GetObjectClass)(env, exception); if ( clazz != NULL ) { char *signature = NULL; /* initing on throw, check */ error = classSignature(clazz, &signature, NULL); LOG_MISC(("Checking specific exception: looking for %s, got %s", initOnException, signature)); if ( (error==JVMTI_ERROR_NONE) && (strcmp(signature, initOnException) == 0)) { LOG_MISC(("Initializing on specific exception")); initialize(env, thread, EI_EXCEPTION); } else { error = AGENT_ERROR_INTERNAL; /* Just to cause restore */ } if ( signature != NULL ) { jvmtiDeallocate(signature); } } else { error = AGENT_ERROR_INTERNAL; /* Just to cause restore */ } /* If initialize didn't happen, we need to restore things */ if ( error != JVMTI_ERROR_NONE ) { /* * Restore exception state from before callback call */ LOG_MISC(("No initialization, didn't find right exception")); if (currentException != NULL) { JNI_FUNC_PTR(env,Throw)(env, currentException); } else { JNI_FUNC_PTR(env,ExceptionClear)(env); } } } LOG_MISC(("END cbEarlyException")); }
/* Change all references to global in the EventInfo struct */ static void saveEventInfoRefs(JNIEnv *env, EventInfo *evinfo) { jthread *pthread; jclass *pclazz; jobject *pobject; jthread thread; jclass clazz; jobject object; char sig; JNI_FUNC_PTR(env,ExceptionClear)(env); if ( evinfo->thread != NULL ) { pthread = &(evinfo->thread); thread = *pthread; *pthread = NULL; saveGlobalRef(env, thread, pthread); } if ( evinfo->clazz != NULL ) { pclazz = &(evinfo->clazz); clazz = *pclazz; *pclazz = NULL; saveGlobalRef(env, clazz, pclazz); } if ( evinfo->object != NULL ) { pobject = &(evinfo->object); object = *pobject; *pobject = NULL; saveGlobalRef(env, object, pobject); } switch (evinfo->ei) { case EI_FIELD_MODIFICATION: if ( evinfo->u.field_modification.field_clazz != NULL ) { pclazz = &(evinfo->u.field_modification.field_clazz); clazz = *pclazz; *pclazz = NULL; saveGlobalRef(env, clazz, pclazz); } sig = evinfo->u.field_modification.signature_type; if ((sig == JDWP_TAG(ARRAY)) || (sig == JDWP_TAG(OBJECT))) { if ( evinfo->u.field_modification.new_value.l != NULL ) { pobject = &(evinfo->u.field_modification.new_value.l); object = *pobject; *pobject = NULL; saveGlobalRef(env, object, pobject); } } break; case EI_FIELD_ACCESS: if ( evinfo->u.field_access.field_clazz != NULL ) { pclazz = &(evinfo->u.field_access.field_clazz); clazz = *pclazz; *pclazz = NULL; saveGlobalRef(env, clazz, pclazz); } break; case EI_EXCEPTION: if ( evinfo->u.exception.catch_clazz != NULL ) { pclazz = &(evinfo->u.exception.catch_clazz); clazz = *pclazz; *pclazz = NULL; saveGlobalRef(env, clazz, pclazz); } break; default: break; } if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) { EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,"ExceptionOccurred"); } }
/** * This function returns true only if it is certain that * all events for the given node in the given stack frame will * be filtered. It is used to optimize stepping. (If this * function returns true the stepping algorithm does not * have to step through every instruction in this stack frame; * instead, it can use more efficient method entry/exit * events. */ jboolean eventFilter_predictFiltering(HandlerNode *node, jclass clazz, char *classname) { JNIEnv *env; jboolean willBeFiltered; Filter *filter; jboolean done; int count; int i; willBeFiltered = JNI_FALSE; env = NULL; filter = FILTERS_ARRAY(node); count = FILTER_COUNT(node); done = JNI_FALSE; for (i = 0; (i < count) && (!done); ++i, ++filter) { switch (filter->modifier) { case JDWP_REQUEST_MODIFIER(ClassOnly): if ( env==NULL ) { env = getEnv(); } if (!JNI_FUNC_PTR(env,IsAssignableFrom)(env, clazz, filter->u.ClassOnly.clazz)) { willBeFiltered = JNI_TRUE; done = JNI_TRUE; } break; case JDWP_REQUEST_MODIFIER(Count): { /* * If preceeding filters have determined that events will * be filtered out, that is fine and we won't get here. * However, the count must be decremented - even if * subsequent filters will filter these events. We * thus must end now unable to predict */ done = JNI_TRUE; break; } case JDWP_REQUEST_MODIFIER(ClassMatch): { if (!patternStringMatch(classname, filter->u.ClassMatch.classPattern)) { willBeFiltered = JNI_TRUE; done = JNI_TRUE; } break; } case JDWP_REQUEST_MODIFIER(ClassExclude): { if (patternStringMatch(classname, filter->u.ClassExclude.classPattern)) { willBeFiltered = JNI_TRUE; done = JNI_TRUE; } break; } } } return willBeFiltered; }
/* * Determine if this event is interesting to this handler. * Do so by checking each of the handler's filters. * Return false if any of the filters fail, * true if the handler wants this event. * Anyone modifying this function should check * eventFilterRestricted_passesUnloadFilter and * eventFilter_predictFiltering as well. * * If shouldDelete is returned true, a count filter has expired * and the corresponding node should be deleted. */ jboolean eventFilterRestricted_passesFilter(JNIEnv *env, char *classname, EventInfo *evinfo, HandlerNode *node, jboolean *shouldDelete) { jthread thread; jclass clazz; jmethodID method; Filter *filter = FILTERS_ARRAY(node); int i; *shouldDelete = JNI_FALSE; thread = evinfo->thread; clazz = evinfo->clazz; method = evinfo->method; /* * Suppress most events if they happen in debug threads */ if ((evinfo->ei != EI_CLASS_PREPARE) && (evinfo->ei != EI_GC_FINISH) && (evinfo->ei != EI_CLASS_LOAD) && threadControl_isDebugThread(thread)) { return JNI_FALSE; } for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) { switch (filter->modifier) { case JDWP_REQUEST_MODIFIER(ThreadOnly): if (!isSameObject(env, thread, filter->u.ThreadOnly.thread)) { return JNI_FALSE; } break; case JDWP_REQUEST_MODIFIER(ClassOnly): /* Class filters catch events in the specified * class and any subclass/subinterface. */ if (!JNI_FUNC_PTR(env,IsAssignableFrom)(env, clazz, filter->u.ClassOnly.clazz)) { return JNI_FALSE; } break; /* This is kinda cheating assumming the event * fields will be in the same locations, but it is * true now. */ case JDWP_REQUEST_MODIFIER(LocationOnly): if (evinfo->method != filter->u.LocationOnly.method || evinfo->location != filter->u.LocationOnly.location || !isSameObject(env, clazz, filter->u.LocationOnly.clazz)) { return JNI_FALSE; } break; case JDWP_REQUEST_MODIFIER(FieldOnly): /* Field watchpoints can be triggered from the * declared class or any subclass/subinterface. */ if ((evinfo->u.field_access.field != filter->u.FieldOnly.field) || !isSameObject(env, evinfo->u.field_access.field_clazz, filter->u.FieldOnly.clazz)) { return JNI_FALSE; } break; case JDWP_REQUEST_MODIFIER(ExceptionOnly): /* do we want caught/uncaught exceptions */ if (!((evinfo->u.exception.catch_clazz == NULL)? filter->u.ExceptionOnly.uncaught : filter->u.ExceptionOnly.caught)) { return JNI_FALSE; } /* do we care about exception class */ if (filter->u.ExceptionOnly.exception != NULL) { jclass exception = evinfo->object; /* do we want this exception class */ if (!JNI_FUNC_PTR(env,IsInstanceOf)(env, exception, filter->u.ExceptionOnly.exception)) { return JNI_FALSE; } } break; case JDWP_REQUEST_MODIFIER(InstanceOnly): { jobject eventInst = eventInstance(evinfo); jobject filterInst = filter->u.InstanceOnly.instance; /* if no error and doesn't match, don't pass * filter */ if (eventInst != NULL && !isSameObject(env, eventInst, filterInst)) { return JNI_FALSE; } break; } case JDWP_REQUEST_MODIFIER(Count): { JDI_ASSERT(filter->u.Count.count > 0); if (--filter->u.Count.count > 0) { return JNI_FALSE; } *shouldDelete = JNI_TRUE; break; } case JDWP_REQUEST_MODIFIER(Conditional): /*** if (... filter->u.Conditional.exprID ...) { return JNI_FALSE; } ***/ break; case JDWP_REQUEST_MODIFIER(ClassMatch): { if (!patternStringMatch(classname, filter->u.ClassMatch.classPattern)) { return JNI_FALSE; } break; } case JDWP_REQUEST_MODIFIER(ClassExclude): { if (patternStringMatch(classname, filter->u.ClassExclude.classPattern)) { return JNI_FALSE; } break; } case JDWP_REQUEST_MODIFIER(Step): if (!isSameObject(env, thread, filter->u.Step.thread)) { return JNI_FALSE; } if (!stepControl_handleStep(env, thread, clazz, method)) { return JNI_FALSE; } break; case JDWP_REQUEST_MODIFIER(SourceNameMatch): { char* desiredNamePattern = filter->u.SourceNameOnly.sourceNamePattern; if (!searchAllSourceNames(env, clazz, desiredNamePattern) == 1) { /* The name isn't in the SDE; try the sourceName in the ref * type */ char *sourceName = 0; jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,GetSourceFileName) (gdata->jvmti, clazz, &sourceName); if (error == JVMTI_ERROR_NONE && sourceName != 0 && patternStringMatch(sourceName, desiredNamePattern)) { // got a hit - report the event jvmtiDeallocate(sourceName); break; } // We have no match, we have no source file name, // or we got a JVM TI error. Don't report the event. jvmtiDeallocate(sourceName); return JNI_FALSE; } break; } default: EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"Invalid filter modifier"); return JNI_FALSE; } } return JNI_TRUE; }