annotate src/share/instrument/JavaExceptions.c @ 3188:5de001f5f8b4

6957378: JMX memory leak Reviewed-by: emcmanus, kevinw
author coffeys
date Fri, 05 Nov 2010 17:15:44 +0000
parents 37a05a11f281
children
rev   line source
duke@0 1 /*
ohair@2486 2 * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
duke@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@0 4 *
duke@0 5 * This code is free software; you can redistribute it and/or modify it
duke@0 6 * under the terms of the GNU General Public License version 2 only, as
ohair@2486 7 * published by the Free Software Foundation. Oracle designates this
duke@0 8 * particular file as subject to the "Classpath" exception as provided
ohair@2486 9 * by Oracle in the LICENSE file that accompanied this code.
duke@0 10 *
duke@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@0 14 * version 2 for more details (a copy is included in the LICENSE file that
duke@0 15 * accompanied this code).
duke@0 16 *
duke@0 17 * You should have received a copy of the GNU General Public License version
duke@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
duke@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@0 20 *
ohair@2486 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@2486 22 * or visit www.oracle.com if you need additional information or have any
ohair@2486 23 * questions.
duke@0 24 */
duke@0 25
duke@0 26 /*
duke@0 27 * Copyright 2003 Wily Technology, Inc.
duke@0 28 */
duke@0 29
duke@0 30 #include <jni.h>
duke@0 31 #include <jvmti.h>
duke@0 32
duke@0 33 #include "JPLISAssert.h"
duke@0 34 #include "Utilities.h"
duke@0 35 #include "JavaExceptions.h"
duke@0 36
duke@0 37 /**
duke@0 38 * This module contains utility routines for manipulating Java throwables
duke@0 39 * and JNIEnv throwable state from native code.
duke@0 40 */
duke@0 41
duke@0 42 static jthrowable sFallbackInternalError = NULL;
duke@0 43
duke@0 44 /*
duke@0 45 * Local forward declarations.
duke@0 46 */
duke@0 47
duke@0 48 /* insist on having a throwable. If we already have one, return it.
duke@0 49 * If not, map to fallback
duke@0 50 */
duke@0 51 jthrowable
duke@0 52 forceFallback(jthrowable potentialException);
duke@0 53
duke@0 54
duke@0 55 jthrowable
duke@0 56 forceFallback(jthrowable potentialException) {
duke@0 57 if ( potentialException == NULL ) {
duke@0 58 return sFallbackInternalError;
duke@0 59 }
duke@0 60 else {
duke@0 61 return potentialException;
duke@0 62 }
duke@0 63 }
duke@0 64
duke@0 65 /**
duke@0 66 * Returns true if it properly sets up a fallback exception
duke@0 67 */
duke@0 68 jboolean
duke@0 69 initializeFallbackError(JNIEnv* jnienv) {
duke@0 70 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 71 sFallbackInternalError = createInternalError(jnienv, NULL);
duke@0 72 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 73 return (sFallbackInternalError != NULL);
duke@0 74 }
duke@0 75
duke@0 76
duke@0 77 /*
duke@0 78 * Map everything to InternalError.
duke@0 79 */
duke@0 80 jthrowable
duke@0 81 mapAllCheckedToInternalErrorMapper( JNIEnv * jnienv,
duke@0 82 jthrowable throwableToMap) {
duke@0 83 jthrowable mappedThrowable = NULL;
duke@0 84 jstring message = NULL;
duke@0 85
duke@0 86 jplis_assert(throwableToMap != NULL);
duke@0 87 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 88 jplis_assert(!isUnchecked(jnienv, throwableToMap));
duke@0 89
duke@0 90 message = getMessageFromThrowable(jnienv, throwableToMap);
duke@0 91 mappedThrowable = createInternalError(jnienv, message);
duke@0 92
duke@0 93 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 94 return mappedThrowable;
duke@0 95 }
duke@0 96
duke@0 97
duke@0 98 jboolean
duke@0 99 checkForThrowable( JNIEnv* jnienv) {
duke@0 100 return (*jnienv)->ExceptionCheck(jnienv);
duke@0 101 }
duke@0 102
duke@0 103 jboolean
duke@0 104 isSafeForJNICalls( JNIEnv * jnienv) {
duke@0 105 return !(*jnienv)->ExceptionCheck(jnienv);
duke@0 106 }
duke@0 107
duke@0 108
duke@0 109 void
duke@0 110 logThrowable( JNIEnv * jnienv) {
duke@0 111 if ( checkForThrowable(jnienv) ) {
duke@0 112 (*jnienv)->ExceptionDescribe(jnienv);
duke@0 113 }
duke@0 114 }
duke@0 115
duke@0 116
duke@0 117
duke@0 118 /**
duke@0 119 * Creates an exception or error with the fully qualified classname (ie java/lang/Error)
duke@0 120 * and message passed to its constructor
duke@0 121 */
duke@0 122 jthrowable
duke@0 123 createThrowable( JNIEnv * jnienv,
duke@0 124 const char * className,
duke@0 125 jstring message) {
duke@0 126 jthrowable exception = NULL;
duke@0 127 jmethodID constructor = NULL;
duke@0 128 jclass exceptionClass = NULL;
duke@0 129 jboolean errorOutstanding = JNI_FALSE;
duke@0 130
duke@0 131 jplis_assert(className != NULL);
duke@0 132 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 133
duke@0 134 /* create new VMError with message from exception */
duke@0 135 exceptionClass = (*jnienv)->FindClass(jnienv, className);
duke@0 136 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 137 jplis_assert(!errorOutstanding);
duke@0 138
duke@0 139 if (!errorOutstanding) {
duke@0 140 constructor = (*jnienv)->GetMethodID( jnienv,
duke@0 141 exceptionClass,
duke@0 142 "<init>",
duke@0 143 "(Ljava/lang/String;)V");
duke@0 144 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 145 jplis_assert(!errorOutstanding);
duke@0 146 }
duke@0 147
duke@0 148 if (!errorOutstanding) {
duke@0 149 exception = (*jnienv)->NewObject(jnienv, exceptionClass, constructor, message);
duke@0 150 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 151 jplis_assert(!errorOutstanding);
duke@0 152 }
duke@0 153
duke@0 154 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 155 return exception;
duke@0 156 }
duke@0 157
duke@0 158 jthrowable
duke@0 159 createInternalError(JNIEnv * jnienv, jstring message) {
duke@0 160 return createThrowable( jnienv,
duke@0 161 "java/lang/InternalError",
duke@0 162 message);
duke@0 163 }
duke@0 164
duke@0 165 jthrowable
duke@0 166 createThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
duke@0 167 const char * throwableClassName = NULL;
duke@0 168 const char * message = NULL;
duke@0 169 jstring messageString = NULL;
duke@0 170
duke@0 171 switch ( errorCode ) {
duke@0 172 case JVMTI_ERROR_NULL_POINTER:
duke@0 173 throwableClassName = "java/lang/NullPointerException";
duke@0 174 break;
duke@0 175
duke@0 176 case JVMTI_ERROR_ILLEGAL_ARGUMENT:
duke@0 177 throwableClassName = "java/lang/IllegalArgumentException";
duke@0 178 break;
duke@0 179
duke@0 180 case JVMTI_ERROR_OUT_OF_MEMORY:
duke@0 181 throwableClassName = "java/lang/OutOfMemoryError";
duke@0 182 break;
duke@0 183
duke@0 184 case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:
duke@0 185 throwableClassName = "java/lang/ClassCircularityError";
duke@0 186 break;
duke@0 187
duke@0 188 case JVMTI_ERROR_FAILS_VERIFICATION:
duke@0 189 throwableClassName = "java/lang/VerifyError";
duke@0 190 break;
duke@0 191
duke@0 192 case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:
duke@0 193 throwableClassName = "java/lang/UnsupportedOperationException";
duke@0 194 message = "class redefinition failed: attempted to add a method";
duke@0 195 break;
duke@0 196
duke@0 197 case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:
duke@0 198 throwableClassName = "java/lang/UnsupportedOperationException";
duke@0 199 message = "class redefinition failed: attempted to change the schema (add/remove fields)";
duke@0 200 break;
duke@0 201
duke@0 202 case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:
duke@0 203 throwableClassName = "java/lang/UnsupportedOperationException";
duke@0 204 message = "class redefinition failed: attempted to change superclass or interfaces";
duke@0 205 break;
duke@0 206
duke@0 207 case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:
duke@0 208 throwableClassName = "java/lang/UnsupportedOperationException";
duke@0 209 message = "class redefinition failed: attempted to delete a method";
duke@0 210 break;
duke@0 211
duke@0 212 case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:
duke@0 213 throwableClassName = "java/lang/UnsupportedOperationException";
duke@0 214 message = "class redefinition failed: attempted to change the class modifiers";
duke@0 215 break;
duke@0 216
duke@0 217 case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
duke@0 218 throwableClassName = "java/lang/UnsupportedOperationException";
duke@0 219 message = "class redefinition failed: attempted to change method modifiers";
duke@0 220 break;
duke@0 221
duke@0 222 case JVMTI_ERROR_UNSUPPORTED_VERSION:
duke@0 223 throwableClassName = "java/lang/UnsupportedClassVersionError";
duke@0 224 break;
duke@0 225
duke@0 226 case JVMTI_ERROR_NAMES_DONT_MATCH:
duke@0 227 throwableClassName = "java/lang/NoClassDefFoundError";
duke@0 228 message = "class names don't match";
duke@0 229 break;
duke@0 230
duke@0 231 case JVMTI_ERROR_INVALID_CLASS_FORMAT:
duke@0 232 throwableClassName = "java/lang/ClassFormatError";
duke@0 233 break;
duke@0 234
duke@0 235 case JVMTI_ERROR_UNMODIFIABLE_CLASS:
duke@0 236 throwableClassName = "java/lang/instrument/UnmodifiableClassException";
duke@0 237 break;
duke@0 238
duke@0 239 case JVMTI_ERROR_INVALID_CLASS:
duke@0 240 throwableClassName = "java/lang/InternalError";
duke@0 241 message = "class redefinition failed: invalid class";
duke@0 242 break;
duke@0 243
duke@0 244 case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED:
duke@0 245 throwableClassName = "java/lang/UnsupportedOperationException";
duke@0 246 message = "unsupported operation";
duke@0 247 break;
duke@0 248
duke@0 249 case JVMTI_ERROR_INTERNAL:
duke@0 250 default:
duke@0 251 throwableClassName = "java/lang/InternalError";
duke@0 252 break;
duke@0 253 }
duke@0 254
duke@0 255 if ( message != NULL ) {
duke@0 256 jboolean errorOutstanding;
duke@0 257
duke@0 258 messageString = (*jnienv)->NewStringUTF(jnienv, message);
duke@0 259 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 260 jplis_assert_msg(!errorOutstanding, "can't create exception java string");
duke@0 261 }
duke@0 262 return createThrowable( jnienv,
duke@0 263 throwableClassName,
duke@0 264 messageString);
duke@0 265
duke@0 266 }
duke@0 267
duke@0 268
duke@0 269 /**
duke@0 270 * Calls toString() on the given message which is the same call made by
duke@0 271 * Exception when passed a throwable to its constructor
duke@0 272 */
duke@0 273 jstring
duke@0 274 getMessageFromThrowable( JNIEnv* jnienv,
duke@0 275 jthrowable exception) {
duke@0 276 jclass exceptionClass = NULL;
duke@0 277 jmethodID method = NULL;
duke@0 278 jstring message = NULL;
duke@0 279 jboolean errorOutstanding = JNI_FALSE;
duke@0 280
duke@0 281 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 282
duke@0 283 /* call getMessage on exception */
duke@0 284 exceptionClass = (*jnienv)->GetObjectClass(jnienv, exception);
duke@0 285 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 286 jplis_assert(!errorOutstanding);
duke@0 287
duke@0 288 if (!errorOutstanding) {
duke@0 289 method = (*jnienv)->GetMethodID(jnienv,
duke@0 290 exceptionClass,
duke@0 291 "toString",
duke@0 292 "()Ljava/lang/String;");
duke@0 293 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 294 jplis_assert(!errorOutstanding);
duke@0 295 }
duke@0 296
duke@0 297 if (!errorOutstanding) {
duke@0 298 message = (*jnienv)->CallObjectMethod(jnienv, exception, method);
duke@0 299 errorOutstanding = checkForAndClearThrowable(jnienv);
duke@0 300 jplis_assert(!errorOutstanding);
duke@0 301 }
duke@0 302
duke@0 303 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 304
duke@0 305 return message;
duke@0 306 }
duke@0 307
duke@0 308
duke@0 309 /**
duke@0 310 * Returns whether the exception given is an unchecked exception:
duke@0 311 * a subclass of Error or RuntimeException
duke@0 312 */
duke@0 313 jboolean
duke@0 314 isUnchecked( JNIEnv* jnienv,
duke@0 315 jthrowable exception) {
duke@0 316 jboolean result = JNI_FALSE;
duke@0 317
duke@0 318 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 319 result = (exception == NULL) ||
duke@0 320 isInstanceofClassName(jnienv, exception, "java/lang/Error") ||
duke@0 321 isInstanceofClassName(jnienv, exception, "java/lang/RuntimeException");
duke@0 322 jplis_assert(isSafeForJNICalls(jnienv));
duke@0 323 return result;
duke@0 324 }
duke@0 325
duke@0 326 /*
duke@0 327 * Returns the current throwable, if any. Clears the throwable state.
duke@0 328 * Clients can use this to preserve the current throwable state on the stack.
duke@0 329 */
duke@0 330 jthrowable
duke@0 331 preserveThrowable(JNIEnv * jnienv) {
duke@0 332 jthrowable result = (*jnienv)->ExceptionOccurred(jnienv);
duke@0 333 if ( result != NULL ) {
duke@0 334 (*jnienv)->ExceptionClear(jnienv);
duke@0 335 }
duke@0 336 return result;
duke@0 337 }
duke@0 338
duke@0 339 /*
duke@0 340 * Installs the supplied throwable into the JNIEnv if the throwable is not null.
duke@0 341 * Clients can use this to preserve the current throwable state on the stack.
duke@0 342 */
duke@0 343 void
duke@0 344 restoreThrowable( JNIEnv * jnienv,
duke@0 345 jthrowable preservedException) {
duke@0 346 throwThrowable( jnienv,
duke@0 347 preservedException);
duke@0 348 return;
duke@0 349 }
duke@0 350
duke@0 351 void
duke@0 352 throwThrowable( JNIEnv * jnienv,
duke@0 353 jthrowable exception) {
duke@0 354 if ( exception != NULL ) {
duke@0 355 jint result = (*jnienv)->Throw(jnienv, exception);
duke@0 356 jplis_assert_msg(result == JNI_OK, "throwThrowable failed to re-throw");
duke@0 357 }
duke@0 358 return;
duke@0 359 }
duke@0 360
duke@0 361
duke@0 362 /*
duke@0 363 * Always clears the JNIEnv throwable state. Returns true if an exception was present
duke@0 364 * before the clearing operation.
duke@0 365 */
duke@0 366 jboolean
duke@0 367 checkForAndClearThrowable( JNIEnv * jnienv) {
duke@0 368 jboolean result = (*jnienv)->ExceptionCheck(jnienv);
duke@0 369 if ( result ) {
duke@0 370 (*jnienv)->ExceptionClear(jnienv);
duke@0 371 }
duke@0 372 return result;
duke@0 373 }
duke@0 374
duke@0 375 /* creates a java.lang.InternalError and installs it into the JNIEnv */
duke@0 376 void
duke@0 377 createAndThrowInternalError(JNIEnv * jnienv) {
duke@0 378 jthrowable internalError = createInternalError( jnienv, NULL);
duke@0 379 throwThrowable(jnienv, forceFallback(internalError));
duke@0 380 }
duke@0 381
duke@0 382 void
duke@0 383 createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
duke@0 384 jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode);
duke@0 385 throwThrowable(jnienv, forceFallback(throwable));
duke@0 386 }
duke@0 387
duke@0 388 void
duke@0 389 mapThrownThrowableIfNecessary( JNIEnv * jnienv,
duke@0 390 CheckedExceptionMapper mapper) {
duke@0 391 jthrowable originalThrowable = NULL;
duke@0 392 jthrowable resultThrowable = NULL;
duke@0 393
duke@0 394 originalThrowable = preserveThrowable(jnienv);
duke@0 395
duke@0 396 /* the throwable is now cleared, so JNI calls are safe */
duke@0 397 if ( originalThrowable != NULL ) {
duke@0 398 /* if there is an exception: we can just throw it if it is unchecked. If checked,
duke@0 399 * we need to map it (mapper is conditional, will vary by usage, hence the callback)
duke@0 400 */
duke@0 401 if ( isUnchecked(jnienv, originalThrowable) ) {
duke@0 402 resultThrowable = originalThrowable;
duke@0 403 }
duke@0 404 else {
duke@0 405 resultThrowable = (*mapper) (jnienv, originalThrowable);
duke@0 406 }
duke@0 407 }
duke@0 408
duke@0 409 /* re-establish the correct throwable */
duke@0 410 if ( resultThrowable != NULL ) {
duke@0 411 throwThrowable(jnienv, forceFallback(resultThrowable));
duke@0 412 }
duke@0 413
duke@0 414 }