# HG changeset patch # User dsimms # Date 1402504449 -7200 # Node ID 4542f853c2ac7db8f85c0cf425852fefc87e71b8 # Parent f38210f84f8c1a757996b7b757d7814e482225c0 8043224: -Xcheck:jni improvements to exception checking and excessive local refs Summary: Warning when not checking exceptions from function that require so, also when local refs expand beyond capacity. Reviewed-by: zgu, coleenp, hseigel diff -r f38210f84f8c -r 4542f853c2ac hotspot/src/share/vm/memory/guardedMemory.hpp --- a/hotspot/src/share/vm/memory/guardedMemory.hpp Wed Jun 11 09:58:23 2014 -0400 +++ b/hotspot/src/share/vm/memory/guardedMemory.hpp Wed Jun 11 18:34:09 2014 +0200 @@ -235,7 +235,7 @@ * @return the size of the user data. */ size_t get_user_size() const { - assert(_base_addr, "Not wrapping any memory"); + assert(_base_addr != NULL, "Not wrapping any memory"); return get_head_guard()->get_user_size(); } @@ -245,7 +245,7 @@ * @return the user data pointer. */ u_char* get_user_ptr() const { - assert(_base_addr, "Not wrapping any memory"); + assert(_base_addr != NULL, "Not wrapping any memory"); return _base_addr + sizeof(GuardHeader); } @@ -281,7 +281,7 @@ memset(get_user_ptr(), ch, get_user_size()); } -public: + public: /** * Return the total size required for wrapping the given user size. * diff -r f38210f84f8c -r 4542f853c2ac hotspot/src/share/vm/prims/jniCheck.cpp --- a/hotspot/src/share/vm/prims/jniCheck.cpp Wed Jun 11 09:58:23 2014 -0400 +++ b/hotspot/src/share/vm/prims/jniCheck.cpp Wed Jun 11 18:34:09 2014 +0200 @@ -53,6 +53,8 @@ # include "jniTypes_ppc.hpp" #endif +// Complain every extra number of unplanned local refs +#define CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD 32 // Heap objects are allowed to be directly referenced only in VM code, // not in native code. @@ -168,12 +170,42 @@ * SUPPORT FUNCTIONS */ +/** + * Check whether or not a programmer has actually checked for exceptions. According + * to the JNI Specification ("jni/spec/design.html#java_exceptions"): + * + * There are two cases where the programmer needs to check for exceptions without + * being able to first check an error code: + * + * - The JNI functions that invoke a Java method return the result of the Java method. + * The programmer must call ExceptionOccurred() to check for possible exceptions + * that occurred during the execution of the Java method. + * + * - Some of the JNI array access functions do not return an error code, but may + * throw an ArrayIndexOutOfBoundsException or ArrayStoreException. + * + * In all other cases, a non-error return value guarantees that no exceptions have been thrown. + */ +static inline void +check_pending_exception(JavaThread* thr) { + if (thr->has_pending_exception()) { + NativeReportJNIWarning(thr, "JNI call made with exception pending"); + } + if (thr->is_pending_jni_exception_check()) { + IN_VM( + tty->print_cr("WARNING in native method: JNI call made without checking exceptions when required to from %s", + thr->get_pending_jni_exception_check()); + thr->print_stack(); + ) + thr->clear_pending_jni_exception_check(); // Just complain once + } +} + + static inline void functionEnterCritical(JavaThread* thr) { - if (thr->has_pending_exception()) { - NativeReportJNIWarning(thr, "JNI call made with exception pending"); - } + check_pending_exception(thr); } static inline void @@ -187,9 +219,7 @@ if (thr->in_critical()) { tty->print_cr("%s", warn_other_function_in_critical); } - if (thr->has_pending_exception()) { - NativeReportJNIWarning(thr, "JNI call made with exception pending"); - } + check_pending_exception(thr); } static inline void @@ -201,9 +231,20 @@ } static inline void -functionExit(JNIEnv *env) +functionExit(JavaThread* thr) { - /* nothing to do at this time */ + JNIHandleBlock* handles = thr->active_handles(); + size_t planned_capacity = handles->get_planned_capacity(); + size_t live_handles = handles->get_number_of_live_handles(); + if (live_handles > planned_capacity) { + IN_VM( + tty->print_cr("WARNING: JNI local refs: %zu, exceeds capacity: %zu", + live_handles, planned_capacity); + thr->print_stack(); + ) + // Complain just the once, reset to current + warn threshold + handles->set_planned_capacity(live_handles + CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD); + } } static inline void @@ -508,7 +549,7 @@ jniCheck::validate_object(thr, loader); ) jclass result = UNCHECKED()->DefineClass(env, name, loader, buf, len); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -520,7 +561,7 @@ jniCheck::validate_class_descriptor(thr, name); ) jclass result = UNCHECKED()->FindClass(env, name); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -532,7 +573,7 @@ jniCheck::validate_object(thr, method); ) jmethodID result = UNCHECKED()->FromReflectedMethod(env, method); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -544,7 +585,7 @@ jniCheck::validate_object(thr, field); ) jfieldID result = UNCHECKED()->FromReflectedField(env, field); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -560,7 +601,7 @@ ) jobject result = UNCHECKED()->ToReflectedMethod(env, cls, methodID, isStatic); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -572,7 +613,7 @@ jniCheck::validate_class(thr, sub, true); ) jclass result = UNCHECKED()->GetSuperclass(env, sub); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -586,7 +627,7 @@ jniCheck::validate_class(thr, sup, true); ) jboolean result = UNCHECKED()->IsAssignableFrom(env, sub, sup); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -601,7 +642,7 @@ ) jobject result = UNCHECKED()->ToReflectedField(env, cls, fieldID, isStatic); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -619,7 +660,7 @@ } ) jint result = UNCHECKED()->Throw(env, obj); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -634,15 +675,16 @@ jniCheck::validate_throwable_klass(thr, k); ) jint result = UNCHECKED()->ThrowNew(env, clazz, msg); - functionExit(env); + functionExit(thr); return result; JNI_END JNI_ENTRY_CHECKED(jthrowable, checked_jni_ExceptionOccurred(JNIEnv *env)) + thr->clear_pending_jni_exception_check(); functionEnterExceptionAllowed(thr); jthrowable result = UNCHECKED()->ExceptionOccurred(env); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -650,22 +692,24 @@ checked_jni_ExceptionDescribe(JNIEnv *env)) functionEnterExceptionAllowed(thr); UNCHECKED()->ExceptionDescribe(env); - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, checked_jni_ExceptionClear(JNIEnv *env)) + thr->clear_pending_jni_exception_check(); functionEnterExceptionAllowed(thr); UNCHECKED()->ExceptionClear(env); - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, checked_jni_FatalError(JNIEnv *env, const char *msg)) + thr->clear_pending_jni_exception_check(); functionEnter(thr); UNCHECKED()->FatalError(env, msg); - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(jint, @@ -675,7 +719,10 @@ if (capacity < 0) NativeReportJNIFatalError(thr, "negative capacity"); jint result = UNCHECKED()->PushLocalFrame(env, capacity); - functionExit(env); + if (result == JNI_OK) { + thr->active_handles()->set_planned_capacity(capacity + CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD); + } + functionExit(thr); return result; JNI_END @@ -684,7 +731,7 @@ jobject result)) functionEnterExceptionAllowed(thr); jobject res = UNCHECKED()->PopLocalFrame(env, result); - functionExit(env); + functionExit(thr); return res; JNI_END @@ -698,7 +745,7 @@ } ) jobject result = UNCHECKED()->NewGlobalRef(env,lobj); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -714,7 +761,7 @@ } ) UNCHECKED()->DeleteGlobalRef(env,gref); - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, @@ -729,7 +776,7 @@ "Invalid local JNI handle passed to DeleteLocalRef"); ) UNCHECKED()->DeleteLocalRef(env, obj); - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(jboolean, @@ -750,7 +797,7 @@ } ) jboolean result = UNCHECKED()->IsSameObject(env,obj1,obj2); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -764,7 +811,7 @@ } ) jobject result = UNCHECKED()->NewLocalRef(env, ref); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -776,7 +823,10 @@ NativeReportJNIFatalError(thr, "negative capacity"); } jint result = UNCHECKED()->EnsureLocalCapacity(env, capacity); - functionExit(env); + if (result == JNI_OK) { + thr->active_handles()->set_planned_capacity(capacity + CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD); + } + functionExit(thr); return result; JNI_END @@ -788,7 +838,7 @@ jniCheck::validate_class(thr, clazz, false); ) jobject result = UNCHECKED()->AllocObject(env,clazz); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -806,7 +856,7 @@ va_start(args, methodID); jobject result = UNCHECKED()->NewObjectV(env,clazz,methodID,args); va_end(args); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -821,7 +871,7 @@ jniCheck::validate_jmethod_id(thr, methodID); ) jobject result = UNCHECKED()->NewObjectV(env,clazz,methodID,args); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -836,7 +886,7 @@ jniCheck::validate_jmethod_id(thr, methodID); ) jobject result = UNCHECKED()->NewObjectA(env,clazz,methodID,args); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -848,7 +898,7 @@ jniCheck::validate_object(thr, obj); ) jclass result = UNCHECKED()->GetObjectClass(env,obj); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -862,7 +912,7 @@ jniCheck::validate_class(thr, clazz, true); ) jboolean result = UNCHECKED()->IsInstanceOf(env,obj,clazz); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -876,7 +926,7 @@ jniCheck::validate_class(thr, clazz, false); ) jmethodID result = UNCHECKED()->GetMethodID(env,clazz,name,sig); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -895,7 +945,8 @@ ResultType result =UNCHECKED()->Call##Result##MethodV(env, obj, methodID, \ args); \ va_end(args); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("Call"#Result"Method"); \ + functionExit(thr); \ return result; \ JNI_END \ \ @@ -910,7 +961,8 @@ ) \ ResultType result = UNCHECKED()->Call##Result##MethodV(env, obj, methodID,\ args); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("Call"#Result"MethodV"); \ + functionExit(thr); \ return result; \ JNI_END \ \ @@ -925,7 +977,8 @@ ) \ ResultType result = UNCHECKED()->Call##Result##MethodA(env, obj, methodID,\ args); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("Call"#Result"MethodA"); \ + functionExit(thr); \ return result; \ JNI_END @@ -952,7 +1005,8 @@ va_start(args,methodID); UNCHECKED()->CallVoidMethodV(env,obj,methodID,args); va_end(args); - functionExit(env); + thr->set_pending_jni_exception_check("CallVoidMethod"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, @@ -965,7 +1019,8 @@ jniCheck::validate_call_object(thr, obj, methodID); ) UNCHECKED()->CallVoidMethodV(env,obj,methodID,args); - functionExit(env); + thr->set_pending_jni_exception_check("CallVoidMethodV"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, @@ -978,7 +1033,8 @@ jniCheck::validate_call_object(thr, obj, methodID); ) UNCHECKED()->CallVoidMethodA(env,obj,methodID,args); - functionExit(env); + thr->set_pending_jni_exception_check("CallVoidMethodA"); + functionExit(thr); JNI_END #define WRAPPER_CallNonvirtualMethod(ResultType, Result) \ @@ -1001,7 +1057,8 @@ methodID,\ args); \ va_end(args); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("CallNonvirtual"#Result"Method"); \ + functionExit(thr); \ return result; \ JNI_END \ \ @@ -1021,7 +1078,8 @@ clazz, \ methodID,\ args); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("CallNonvirtual"#Result"MethodV"); \ + functionExit(thr); \ return result; \ JNI_END \ \ @@ -1041,7 +1099,8 @@ clazz, \ methodID,\ args); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("CallNonvirtual"#Result"MethodA"); \ + functionExit(thr); \ return result; \ JNI_END @@ -1070,7 +1129,8 @@ va_start(args,methodID); UNCHECKED()->CallNonvirtualVoidMethodV(env,obj,clazz,methodID,args); va_end(args); - functionExit(env); + thr->set_pending_jni_exception_check("CallNonvirtualVoidMethod"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, @@ -1085,7 +1145,8 @@ jniCheck::validate_call_class(thr, clazz, methodID); ) UNCHECKED()->CallNonvirtualVoidMethodV(env,obj,clazz,methodID,args); - functionExit(env); + thr->set_pending_jni_exception_check("CallNonvirtualVoidMethodV"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, @@ -1100,7 +1161,8 @@ jniCheck::validate_call_class(thr, clazz, methodID); ) UNCHECKED()->CallNonvirtualVoidMethodA(env,obj,clazz,methodID,args); - functionExit(env); + thr->set_pending_jni_exception_check("CallNonvirtualVoidMethodA"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(jfieldID, @@ -1113,7 +1175,7 @@ jniCheck::validate_class(thr, clazz, false); ) jfieldID result = UNCHECKED()->GetFieldID(env,clazz,name,sig); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1127,7 +1189,7 @@ checkInstanceFieldID(thr, fieldID, obj, FieldType); \ ) \ ReturnType result = UNCHECKED()->Get##Result##Field(env,obj,fieldID); \ - functionExit(env); \ + functionExit(thr); \ return result; \ JNI_END @@ -1152,7 +1214,7 @@ checkInstanceFieldID(thr, fieldID, obj, FieldType); \ ) \ UNCHECKED()->Set##Result##Field(env,obj,fieldID,val); \ - functionExit(env); \ + functionExit(thr); \ JNI_END WRAPPER_SetField(jobject, Object, T_OBJECT) @@ -1176,7 +1238,7 @@ jniCheck::validate_class(thr, clazz, false); ) jmethodID result = UNCHECKED()->GetStaticMethodID(env,clazz,name,sig); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1198,7 +1260,8 @@ methodID, \ args); \ va_end(args); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("CallStatic"#Result"Method"); \ + functionExit(thr); \ return result; \ JNI_END \ \ @@ -1216,7 +1279,8 @@ clazz, \ methodID, \ args); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("CallStatic"#Result"MethodV"); \ + functionExit(thr); \ return result; \ JNI_END \ \ @@ -1234,7 +1298,8 @@ clazz, \ methodID, \ args); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("CallStatic"#Result"MethodA"); \ + functionExit(thr); \ return result; \ JNI_END @@ -1262,7 +1327,8 @@ va_start(args,methodID); UNCHECKED()->CallStaticVoidMethodV(env,cls,methodID,args); va_end(args); - functionExit(env); + thr->set_pending_jni_exception_check("CallStaticVoidMethod"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, @@ -1276,7 +1342,8 @@ jniCheck::validate_class(thr, cls, false); ) UNCHECKED()->CallStaticVoidMethodV(env,cls,methodID,args); - functionExit(env); + thr->set_pending_jni_exception_check("CallStaticVoidMethodV"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, @@ -1290,7 +1357,8 @@ jniCheck::validate_class(thr, cls, false); ) UNCHECKED()->CallStaticVoidMethodA(env,cls,methodID,args); - functionExit(env); + thr->set_pending_jni_exception_check("CallStaticVoidMethodA"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(jfieldID, @@ -1303,7 +1371,7 @@ jniCheck::validate_class(thr, clazz, false); ) jfieldID result = UNCHECKED()->GetStaticFieldID(env,clazz,name,sig); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1320,7 +1388,7 @@ ReturnType result = UNCHECKED()->GetStatic##Result##Field(env, \ clazz, \ fieldID); \ - functionExit(env); \ + functionExit(thr); \ return result; \ JNI_END @@ -1346,7 +1414,7 @@ checkStaticFieldID(thr, fieldID, clazz, FieldType); \ ) \ UNCHECKED()->SetStatic##Result##Field(env,clazz,fieldID,value); \ - functionExit(env); \ + functionExit(thr); \ JNI_END WRAPPER_SetStaticField(jobject, Object, T_OBJECT) @@ -1366,7 +1434,7 @@ jsize len)) functionEnter(thr); jstring result = UNCHECKED()->NewString(env,unicode,len); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1378,7 +1446,7 @@ checkString(thr, str); ) jsize result = UNCHECKED()->GetStringLength(env,str); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1407,7 +1475,7 @@ // Note that the dtrace arguments for the allocated memory will not match up with this solution. FreeHeap((char*)result); } - functionExit(env); + functionExit(thr); return new_result; JNI_END @@ -1442,7 +1510,7 @@ UNCHECKED()->ReleaseStringChars(env, str, (const jchar*) guarded.release_for_freeing()); } - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(jstring, @@ -1450,7 +1518,7 @@ const char *utf)) functionEnter(thr); jstring result = UNCHECKED()->NewStringUTF(env,utf); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1462,7 +1530,7 @@ checkString(thr, str); ) jsize result = UNCHECKED()->GetStringUTFLength(env,str); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1490,7 +1558,7 @@ // Note that the dtrace arguments for the allocated memory will not match up with this solution. FreeHeap((char*)result, mtInternal); } - functionExit(env); + functionExit(thr); return new_result; JNI_END @@ -1525,7 +1593,7 @@ UNCHECKED()->ReleaseStringUTFChars(env, str, (const char*) guarded.release_for_freeing()); } - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(jsize, @@ -1536,7 +1604,7 @@ check_is_array(thr, array); ) jsize result = UNCHECKED()->GetArrayLength(env,array); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1547,7 +1615,7 @@ jobject init)) functionEnter(thr); jobjectArray result = UNCHECKED()->NewObjectArray(env,len,clazz,init); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1560,7 +1628,8 @@ check_is_obj_array(thr, array); ) jobject result = UNCHECKED()->GetObjectArrayElement(env,array,index); - functionExit(env); + thr->set_pending_jni_exception_check("GetObjectArrayElement"); + functionExit(thr); return result; JNI_END @@ -1574,7 +1643,8 @@ check_is_obj_array(thr, array); ) UNCHECKED()->SetObjectArrayElement(env,array,index,val); - functionExit(env); + thr->set_pending_jni_exception_check("SetObjectArrayElement"); + functionExit(thr); JNI_END #define WRAPPER_NewScalarArray(Return, Result) \ @@ -1583,7 +1653,7 @@ jsize len)) \ functionEnter(thr); \ Return result = UNCHECKED()->New##Result##Array(env,len); \ - functionExit(env); \ + functionExit(thr); \ return (Return) result; \ JNI_END @@ -1611,7 +1681,7 @@ if (result != NULL) { \ result = (ElementType *) check_jni_wrap_copy_array(thr, array, result); \ } \ - functionExit(env); \ + functionExit(thr); \ return result; \ JNI_END @@ -1639,7 +1709,7 @@ ElementType* orig_result = (ElementType *) check_wrapped_array_release( \ thr, "checked_jni_Release"#Result"ArrayElements", array, elems, mode); \ UNCHECKED()->Release##Result##ArrayElements(env, array, orig_result, mode); \ - functionExit(env); \ + functionExit(thr); \ JNI_END WRAPPER_ReleaseScalarArrayElements(T_BOOLEAN,jboolean, Boolean, bool) @@ -1663,7 +1733,8 @@ check_primitive_array_type(thr, array, ElementTag); \ ) \ UNCHECKED()->Get##Result##ArrayRegion(env,array,start,len,buf); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("Get"#Result"ArrayRegion"); \ + functionExit(thr); \ JNI_END WRAPPER_GetScalarArrayRegion(T_BOOLEAN, jboolean, Boolean) @@ -1687,7 +1758,8 @@ check_primitive_array_type(thr, array, ElementTag); \ ) \ UNCHECKED()->Set##Result##ArrayRegion(env,array,start,len,buf); \ - functionExit(env); \ + thr->set_pending_jni_exception_check("Set"#Result"ArrayRegion"); \ + functionExit(thr); \ JNI_END WRAPPER_SetScalarArrayRegion(T_BOOLEAN, jboolean, Boolean) @@ -1706,7 +1778,7 @@ jint nMethods)) functionEnter(thr); jint result = UNCHECKED()->RegisterNatives(env,clazz,methods,nMethods); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1715,7 +1787,7 @@ jclass clazz)) functionEnter(thr); jint result = UNCHECKED()->UnregisterNatives(env,clazz); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1727,7 +1799,7 @@ jniCheck::validate_object(thr, obj); ) jint result = UNCHECKED()->MonitorEnter(env,obj); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1739,7 +1811,7 @@ jniCheck::validate_object(thr, obj); ) jint result = UNCHECKED()->MonitorExit(env,obj); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1748,7 +1820,7 @@ JavaVM **vm)) functionEnter(thr); jint result = UNCHECKED()->GetJavaVM(env,vm); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1763,7 +1835,8 @@ checkString(thr, str); ) UNCHECKED()->GetStringRegion(env, str, start, len, buf); - functionExit(env); + thr->set_pending_jni_exception_check("GetStringRegion"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void, @@ -1777,7 +1850,8 @@ checkString(thr, str); ) UNCHECKED()->GetStringUTFRegion(env, str, start, len, buf); - functionExit(env); + thr->set_pending_jni_exception_check("GetStringUTFRegion"); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(void *, @@ -1792,7 +1866,7 @@ if (result != NULL) { result = check_jni_wrap_copy_array(thr, array, result); } - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1808,7 +1882,7 @@ // Check the element array... void* orig_result = check_wrapped_array_release(thr, "ReleasePrimitiveArrayCritical", array, carray, mode); UNCHECKED()->ReleasePrimitiveArrayCritical(env, array, orig_result, mode); - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(const jchar*, @@ -1820,7 +1894,7 @@ checkString(thr, string); ) const jchar *result = UNCHECKED()->GetStringCritical(env, string, isCopy); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1836,7 +1910,7 @@ * string parameter as a minor sanity check */ UNCHECKED()->ReleaseStringCritical(env, str, chars); - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(jweak, @@ -1849,7 +1923,7 @@ } ) jweak result = UNCHECKED()->NewWeakGlobalRef(env, obj); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1858,14 +1932,15 @@ jweak ref)) functionEnterExceptionAllowed(thr); UNCHECKED()->DeleteWeakGlobalRef(env, ref); - functionExit(env); + functionExit(thr); JNI_END JNI_ENTRY_CHECKED(jboolean, checked_jni_ExceptionCheck(JNIEnv *env)) + thr->clear_pending_jni_exception_check(); functionEnterExceptionAllowed(thr); jboolean result = UNCHECKED()->ExceptionCheck(env); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1875,7 +1950,7 @@ jlong capacity)) functionEnter(thr); jobject result = UNCHECKED()->NewDirectByteBuffer(env, address, capacity); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1884,7 +1959,7 @@ jobject buf)) functionEnter(thr); void* result = UNCHECKED()->GetDirectBufferAddress(env, buf); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1893,7 +1968,7 @@ jobject buf)) functionEnter(thr); jlong result = UNCHECKED()->GetDirectBufferCapacity(env, buf); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1906,7 +1981,7 @@ jniCheck::validate_object(thr, obj); ) jobjectRefType result = UNCHECKED()->GetObjectRefType(env, obj); - functionExit(env); + functionExit(thr); return result; JNI_END @@ -1915,7 +1990,7 @@ checked_jni_GetVersion(JNIEnv *env)) functionEnter(thr); jint result = UNCHECKED()->GetVersion(env); - functionExit(env); + functionExit(thr); return result; JNI_END diff -r f38210f84f8c -r 4542f853c2ac hotspot/src/share/vm/runtime/jniHandles.cpp --- a/hotspot/src/share/vm/runtime/jniHandles.cpp Wed Jun 11 09:58:23 2014 -0400 +++ b/hotspot/src/share/vm/runtime/jniHandles.cpp Wed Jun 11 18:34:09 2014 +0200 @@ -298,6 +298,7 @@ block->_top = 0; block->_next = NULL; block->_pop_frame_link = NULL; + block->_planned_capacity = block_size_in_oops; // _last, _free_list & _allocate_before_rebuild initialized in allocate_handle debug_only(block->_last = NULL); debug_only(block->_free_list = NULL); @@ -531,6 +532,12 @@ return result; } +const size_t JNIHandleBlock::get_number_of_live_handles() { + CountHandleClosure counter; + oops_do(&counter); + return counter.count(); +} + // This method is not thread-safe, i.e., must be called while holding a lock on the // structure. long JNIHandleBlock::memory_usage() const { diff -r f38210f84f8c -r 4542f853c2ac hotspot/src/share/vm/runtime/jniHandles.hpp --- a/hotspot/src/share/vm/runtime/jniHandles.hpp Wed Jun 11 09:58:23 2014 -0400 +++ b/hotspot/src/share/vm/runtime/jniHandles.hpp Wed Jun 11 18:34:09 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2014, 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 @@ -112,6 +112,9 @@ oop* _free_list; // Handle free list int _allocate_before_rebuild; // Number of blocks to allocate before rebuilding free list + // Check JNI, "planned capacity" for current frame (or push/ensure) + size_t _planned_capacity; + #ifndef PRODUCT JNIHandleBlock* _block_list_link; // Link for list below static JNIHandleBlock* _block_list; // List of all allocated blocks (for debugging only) @@ -152,6 +155,11 @@ // Traversal of weak handles. Unreachable oops are cleared. void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f); + // Checked JNI support + void set_planned_capacity(size_t planned_capacity) { _planned_capacity = planned_capacity; } + const size_t get_planned_capacity() { return _planned_capacity; } + const size_t get_number_of_live_handles(); + // Debugging bool chain_contains(jobject handle) const; // Does this block or following blocks contain handle bool contains(jobject handle) const; // Does this block contain handle diff -r f38210f84f8c -r 4542f853c2ac hotspot/src/share/vm/runtime/thread.cpp --- a/hotspot/src/share/vm/runtime/thread.cpp Wed Jun 11 09:58:23 2014 -0400 +++ b/hotspot/src/share/vm/runtime/thread.cpp Wed Jun 11 18:34:09 2014 +0200 @@ -1448,6 +1448,7 @@ _thread_stat = new ThreadStatistics(); _blocked_on_compilation = false; _jni_active_critical = 0; + _pending_jni_exception_check_fn = NULL; _do_not_unlock_if_synchronized = false; _cached_monitor_info = NULL; _parker = Parker::Allocate(this) ; diff -r f38210f84f8c -r 4542f853c2ac hotspot/src/share/vm/runtime/thread.hpp --- a/hotspot/src/share/vm/runtime/thread.hpp Wed Jun 11 09:58:23 2014 -0400 +++ b/hotspot/src/share/vm/runtime/thread.hpp Wed Jun 11 18:34:09 2014 +0200 @@ -915,6 +915,9 @@ // support for JNI critical regions jint _jni_active_critical; // count of entries into JNI critical region + // Checked JNI: function name requires exception check + char* _pending_jni_exception_check_fn; + // For deadlock detection. int _depth_first_number; @@ -1400,6 +1403,12 @@ assert(_jni_active_critical >= 0, "JNI critical nesting problem?"); } + // Checked JNI, is the programmer required to check for exceptions, specify which function name + bool is_pending_jni_exception_check() const { return _pending_jni_exception_check_fn != NULL; } + void clear_pending_jni_exception_check() { _pending_jni_exception_check_fn = NULL; } + const char* get_pending_jni_exception_check() const { return _pending_jni_exception_check_fn; } + void set_pending_jni_exception_check(const char* fn_name) { _pending_jni_exception_check_fn = (char*) fn_name; } + // For deadlock detection int depth_first_number() { return _depth_first_number; } void set_depth_first_number(int dfn) { _depth_first_number = dfn; }