| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /** |
| * @author Intel, Pavel Afremov |
| */ |
| |
| #define LOG_DOMAIN "exn" |
| #include "clog.h" |
| |
| #include "vtable.h" |
| #include "heap.h" |
| #include "classloader.h" |
| #include "exceptions.h" |
| #include "exceptions_impl.h" |
| #include "exceptions_jit.h" |
| #include "ini.h" |
| #include "interpreter.h" |
| #include "jni_utils.h" |
| #include "m2n.h" |
| #include "object_handles.h" |
| #include "vm_arrays.h" |
| #include "vm_strings.h" |
| #include "port_thread.h" |
| #include "cci.h" |
| #include "ExpandableMemBlock.h" |
| |
| bool exn_raised() |
| { |
| // no need to disable gc for simple null equality check |
| vm_thread_t vm_thread = p_TLS_vmthread; |
| return ((NULL != vm_thread->thread_exception.exc_object) |
| || (NULL != vm_thread->thread_exception.exc_class)); |
| } |
| |
| |
| //FIXME LAZY EXCEPTION (2006.05.06) |
| //Find all usage and change to lazy use |
| jthrowable exn_get() |
| { |
| assert(hythread_is_suspend_enabled()); |
| |
| // we can check heap references for equality to NULL |
| // without disabling gc, because GC wouldn't change |
| // null to non-null and vice versa. |
| vm_thread_t vm_thread = p_TLS_vmthread; |
| if ((NULL == vm_thread->thread_exception.exc_object) |
| && (NULL == vm_thread->thread_exception.exc_class)) { |
| return NULL; |
| } |
| |
| // returned value which will contains jthrowable value of |
| // curent thread exception |
| jobject exc; |
| |
| if (NULL != vm_thread->thread_exception.exc_object) { |
| tmn_suspend_disable(); |
| exc = oh_allocate_local_handle(); |
| exc->object = (ManagedObject *) vm_thread->thread_exception.exc_object; |
| tmn_suspend_enable(); |
| } else if (NULL != vm_thread->thread_exception.exc_class) { |
| exc = exn_create((Exception*)&(vm_thread->thread_exception)); |
| } else { |
| LDIE(59, "It's impossible internal error in exception handling."); |
| } |
| return exc; |
| } // exn_get |
| |
| Class* exn_get_class() { |
| // we can check heap references for equality to NULL |
| // without disabling gc, because GC wouldn't change |
| // null to non-null and vice versa. |
| vm_thread_t vm_thread = p_TLS_vmthread; |
| if ((NULL == vm_thread->thread_exception.exc_object) |
| && (NULL == vm_thread->thread_exception.exc_class)) { |
| return NULL; |
| } |
| |
| Class* result; |
| |
| if (NULL != vm_thread->thread_exception.exc_object) { |
| tmn_suspend_disable_recursive(); |
| ManagedObject* exn = vm_thread->thread_exception.exc_object; |
| result = exn->vt()->clss; |
| tmn_suspend_enable_recursive(); |
| } else if (NULL != vm_thread->thread_exception.exc_class) { |
| result = vm_thread->thread_exception.exc_class; |
| } else { |
| LDIE(59, "It's impossible internal error in exception handling."); |
| } |
| return result; |
| } |
| |
| const char* exn_get_name() { |
| Class* exc_class = exn_get_class(); |
| |
| if (NULL == exc_class) { |
| return NULL; |
| } |
| |
| return exc_class->get_name()->bytes; |
| } |
| |
| void exn_clear() |
| { |
| tmn_suspend_disable_recursive(); |
| clear_exception_internal(); |
| tmn_suspend_enable_recursive(); |
| |
| // This will restore quard stack if needed |
| if (p_TLS_vmthread->restore_guard_page) { |
| int res = port_thread_restore_guard_page(); |
| |
| // if guard stack can't be restored raise SOE |
| if (res != 0) { |
| Global_Env *env = VM_Global_State::loader_env; |
| exn_raise_by_class(env->java_lang_StackOverflowError_Class); |
| } |
| |
| p_TLS_vmthread->restore_guard_page = false; |
| } |
| } |
| |
| bool is_unwindable() |
| { |
| M2nFrame* lastFrame = m2n_get_last_frame(); |
| return !(interpreter_enabled() || (!lastFrame) |
| || (m2n_get_frame_type(lastFrame) & FRAME_NON_UNWINDABLE)); |
| } |
| |
| bool set_unwindable(bool unwindable) |
| { |
| M2nFrame* lastFrame = m2n_get_last_frame(); |
| |
| if (interpreter_enabled() || (!lastFrame)) { |
| assert(!unwindable); |
| return false; |
| } |
| |
| int lastFrameType = m2n_get_frame_type(lastFrame); |
| bool previousValue = !(lastFrameType & FRAME_NON_UNWINDABLE); |
| |
| if (unwindable) { |
| lastFrameType &= ~FRAME_NON_UNWINDABLE; |
| } else { |
| lastFrameType |= FRAME_NON_UNWINDABLE; |
| } |
| m2n_set_frame_type( lastFrame, (frame_type) lastFrameType); |
| return previousValue; |
| } |
| |
| jthrowable exn_create(Exception* exception) { |
| assert(hythread_is_suspend_enabled()); |
| return create_exception(exception); |
| } |
| |
| jthrowable exn_create(Class* exc_class) |
| { |
| return exn_create(exc_class, NULL , NULL); |
| } |
| |
| jthrowable exn_create(Class* exc_class, jthrowable exc_cause) |
| { |
| return exn_create(exc_class, NULL, exc_cause); |
| } |
| |
| jthrowable exn_create(Class* exc_class, const char* exc_message) |
| { |
| return exn_create(exc_class, exc_message , NULL); |
| } |
| |
| jthrowable exn_create(Class* exc_class, const char* exc_message, jthrowable exc_cause) |
| { |
| ASSERT_RAISE_AREA; |
| assert(hythread_is_suspend_enabled()); |
| jthrowable exc_object = create_exception(exc_class, exc_message, exc_cause); |
| |
| if (exc_object == NULL) { |
| exc_object = create_exception(exc_class, exc_message , NULL); |
| |
| if (exc_object == NULL) { |
| return NULL; |
| } |
| init_cause(exc_object, exc_cause); |
| } |
| return exc_object; |
| } |
| |
| jthrowable exn_create(const char* exc_name) |
| { |
| return exn_create(exc_name, NULL, NULL); |
| } |
| |
| jthrowable exn_create(const char* exc_name, jthrowable exc_cause) |
| { |
| return exn_create(exc_name, NULL, exc_cause); |
| } // exn_create(const char* exc_name, jthrowable cause) |
| |
| jthrowable exn_create(const char* exc_name,const char *exc_message) |
| { |
| return exn_create(exc_name, exc_message, NULL); |
| } // exn_create(const char *exception_name, const char *exc_message) |
| |
| jthrowable exn_create(const char *exc_name, const char *exc_message, jthrowable cause) |
| { |
| ASSERT_RAISE_AREA; |
| assert(hythread_is_suspend_enabled()); |
| Class *exc_class = get_exc_class(exc_name); |
| |
| if (exc_class == NULL) { |
| assert(exn_raised()); |
| return NULL; |
| } |
| return exn_create(exc_class, exc_message, cause); |
| } // exn_create |
| |
| void exn_throw_object(jthrowable exc_object) { |
| assert(is_unwindable()); |
| exn_throw_object_internal(exc_object); |
| } |
| |
| void exn_throw_by_class(Class* exc_class) |
| { |
| exn_throw_by_class(exc_class, NULL, NULL); |
| } |
| |
| void exn_throw_by_class(Class* exc_class, jthrowable exc_cause) |
| { |
| exn_throw_by_class(exc_class, NULL, exc_cause); |
| } |
| |
| void exn_throw_by_class(Class* exc_class, const char* exc_message) |
| { |
| exn_throw_by_class(exc_class, exc_message, NULL); |
| } |
| |
| void exn_throw_by_class(Class* exc_class, const char* exc_message, |
| jthrowable exc_cause) |
| { |
| assert(is_unwindable()); |
| |
| exn_throw_by_class_internal(exc_class, exc_message, exc_cause); |
| } |
| |
| void exn_throw_by_name(const char* exc_name) |
| { |
| exn_throw_by_name(exc_name, NULL, NULL); |
| } |
| |
| void exn_throw_by_name(const char* exc_name, jthrowable exc_cause) |
| { |
| exn_throw_by_name(exc_name, NULL, exc_cause); |
| } |
| |
| void exn_throw_by_name(const char* exc_name, const char* exc_message) |
| { |
| exn_throw_by_name(exc_name, exc_message, NULL); |
| } |
| |
| void exn_throw_by_name(const char* exc_name, const char* exc_message, |
| jthrowable exc_cause) |
| { |
| assert(is_unwindable()); |
| |
| exn_throw_by_name_internal(exc_name, exc_message, exc_cause); |
| } |
| |
| void exn_raise_object(jthrowable exc_object) |
| { |
| assert(!is_unwindable()); |
| assert(exc_object); |
| exn_raise_object_internal(exc_object); |
| } |
| |
| void exn_raise_by_class(Class* exc_class) |
| { |
| exn_raise_by_class(exc_class, NULL, NULL); |
| } |
| |
| void exn_raise_by_class(Class* exc_class, jthrowable exc_cause) |
| { |
| exn_raise_by_class(exc_class, NULL, exc_cause); |
| } |
| |
| void exn_raise_by_class(Class* exc_class, const char* exc_message) |
| { |
| exn_raise_by_class(exc_class, exc_message, NULL); |
| } |
| |
| void exn_raise_by_class(Class* exc_class, const char* exc_message, |
| jthrowable exc_cause) |
| { |
| assert(!is_unwindable()); |
| assert(exc_class); |
| exn_raise_by_class_internal(exc_class, exc_message, exc_cause); |
| } |
| |
| void exn_raise_by_name(const char* exc_name) |
| { |
| exn_raise_by_name(exc_name, NULL, NULL); |
| } |
| |
| void exn_raise_by_name(const char* exc_name, jthrowable exc_cause) |
| { |
| exn_raise_by_name(exc_name, NULL, exc_cause); |
| } |
| |
| void exn_raise_by_name(const char* exc_name, const char* exc_message) |
| { |
| exn_raise_by_name(exc_name, exc_message, NULL); |
| } |
| |
| void exn_raise_by_name(const char* exc_name, const char* exc_message, |
| jthrowable exc_cause) |
| { |
| assert(hythread_is_suspend_enabled()); |
| assert(!is_unwindable()); |
| assert(exc_name); |
| exn_raise_by_name_internal(exc_name, exc_message, exc_cause); |
| } |
| |
| // FIXME moove to exception_impl.cpp |
| static void check_pop_frame(ManagedObject *exn) { |
| if (exn == VM_Global_State::loader_env->popFrameException->object) { |
| exn_clear(); |
| frame_type type = m2n_get_frame_type(m2n_get_last_frame()); |
| |
| if (FRAME_POP_NOW == (FRAME_POP_MASK & type)) { |
| jvmti_jit_do_pop_frame(); |
| } |
| } |
| } |
| |
| // function can be cold from suspen enabled and disabled mode |
| void exn_rethrow() |
| { |
| // exception is throwing, so suspend can be disabeled without following enabling |
| if (hythread_is_suspend_enabled()) { |
| tmn_suspend_disable(); |
| } |
| |
| assert(!hythread_is_suspend_enabled()); |
| |
| BEGIN_RAISE_AREA; |
| |
| #ifndef VM_LAZY_EXCEPTION |
| ManagedObject *exn = get_exception_object_internal(); |
| assert(exn); |
| clear_exception_internal(); |
| |
| check_pop_frame(exn); |
| |
| exn_throw_for_JIT(exn, NULL, NULL, NULL, NULL); |
| #else |
| vm_thread_t vm_thread = p_TLS_vmthread; |
| if (NULL != vm_thread->thread_exception.exc_object) { |
| ManagedObject* exn_mng_object = vm_thread->thread_exception.exc_object; |
| clear_exception_internal(); |
| |
| check_pop_frame(exn_mng_object); |
| |
| exn_throw_for_JIT(exn_mng_object, NULL, NULL, NULL, NULL); |
| } else if (NULL != vm_thread->thread_exception.exc_class) { |
| Class * exc_class = vm_thread->thread_exception.exc_class; |
| const char* exc_message = vm_thread->thread_exception.exc_message; |
| jthrowable exc_cause = NULL; |
| |
| if (vm_thread->thread_exception.exc_cause){ |
| exc_cause = oh_allocate_local_handle(); |
| exc_cause->object = vm_thread->thread_exception.exc_cause; |
| } |
| clear_exception_internal(); |
| |
| exn_throw_by_class_internal(exc_class, exc_message, exc_cause); |
| } else { |
| LDIE(60, "There is no exception."); |
| } |
| #endif |
| DIE(("It's Unreachable place.")); |
| |
| END_RAISE_AREA; |
| } //exn_rethrow |
| |
| void exn_rethrow_if_pending() |
| { |
| if (exn_raised()) { |
| exn_rethrow(); |
| } |
| } //exn_rethrow_if_pending |
| |
| ////////////////////////////////////////////////////////////////////////// |
| // Java Stack Trace Utilities |
| #define STF_AS_JLONG 5 |
| |
| // prints stackTrace via java |
| inline void exn_java_print_stack_trace(FILE * UNREF f, jthrowable exc) |
| { |
| assert(hythread_is_suspend_enabled()); |
| // finds java environment |
| JNIEnv *jenv = p_TLS_vmthread->jni_env; |
| |
| // finds class of Throwable |
| jclass throwableClazz = FindClass(jenv, VM_Global_State::loader_env->JavaLangThrowable_String); |
| |
| // tries to print stackTrace via java |
| jmethodID printStackTraceID = |
| GetMethodID(jenv, throwableClazz, "printStackTrace", "()V"); |
| CallVoidMethod(jenv, exc, printStackTraceID); |
| } |
| |
| // prints stackTrace via jni |
| inline void exn_jni_print_stack_trace(FILE * f, jthrowable exc) |
| { |
| assert(hythread_is_suspend_enabled()); |
| // finds java environment |
| JNIEnv *jenv = p_TLS_vmthread->jni_env; |
| |
| // finds class of Throwable |
| jclass throwableClazz = FindClass(jenv, VM_Global_State::loader_env->JavaLangThrowable_String); |
| |
| // print exception message |
| if (ExceptionCheck(jenv)) |
| return; |
| |
| jmethodID getMessageId = GetMethodID(jenv, throwableClazz, "getMessage", |
| "()Ljava/lang/String;"); |
| jstring message = CallObjectMethod(jenv, exc, getMessageId); |
| |
| if (ExceptionCheck(jenv)) |
| return; |
| |
| tmn_suspend_disable(); |
| const char* exceptionNameChars = exc->object->vt()->clss->get_name()->bytes; |
| tmn_suspend_enable(); |
| const char *messageChars = GetStringUTFChars(jenv, message, NULL); |
| fprintf(f, "\n%s : %s\n", exceptionNameChars, messageChars); |
| |
| // gets stack trace to print it |
| jmethodID getStackTraceID = |
| GetMethodID(jenv, throwableClazz, "getStackTrace", |
| "()[Ljava/lang/StackTraceElement;"); |
| jobjectArray stackTrace = CallObjectMethod(jenv, exc, getStackTraceID); |
| |
| if (ExceptionCheck(jenv) || !stackTrace) |
| return; |
| int stackTraceLenth = GetArrayLength(jenv, stackTrace); |
| |
| // finds all required JNI IDs from StackTraceElement to avoid finding in cycle |
| jclass stackTraceElementClazz = struct_Class_to_java_lang_Class_Handle( |
| VM_Global_State::loader_env->java_lang_StackTraceElement_Class); |
| jmethodID getClassNameId = |
| GetMethodID(jenv, stackTraceElementClazz, "getClassName", |
| "()Ljava/lang/String;"); |
| jmethodID getMethodNameId = |
| GetMethodID(jenv, stackTraceElementClazz, "getMethodName", |
| "()Ljava/lang/String;"); |
| jmethodID getFileNameId = |
| GetMethodID(jenv, stackTraceElementClazz, "getFileName", |
| "()Ljava/lang/String;"); |
| jmethodID getLineNumberId = |
| GetMethodID(jenv, stackTraceElementClazz, "getLineNumber", "()I"); |
| jmethodID isNativeMethodId = |
| GetMethodID(jenv, stackTraceElementClazz, "isNativeMethod", "()Z"); |
| |
| // prints stack trace line by line |
| // Afremov Pavel 20050120 it's necessary to skip some line sof stack trace ganaration |
| for (int itemIndex = 0; itemIndex < stackTraceLenth; itemIndex++) { |
| // gets stack trace element (one line in stack trace) |
| jobject stackTraceElement = |
| GetObjectArrayElement(jenv, stackTrace, itemIndex); |
| |
| // prints begin of stack trace line |
| fprintf(f, " at "); |
| |
| // gets and prints information about class and method |
| jstring className = |
| CallObjectMethod(jenv, stackTraceElement, getClassNameId); |
| |
| if (ExceptionCheck(jenv)) |
| return; |
| jstring methodName = |
| CallObjectMethod(jenv, stackTraceElement, getMethodNameId); |
| |
| if (ExceptionCheck(jenv)) |
| return; |
| const char *classNameChars = |
| GetStringUTFChars(jenv, className, NULL); |
| fprintf(f, "%s.", classNameChars); |
| const char *methodNameChars = |
| GetStringUTFChars(jenv, methodName, NULL); |
| fprintf(f, "%s", methodNameChars); |
| |
| // gets information about java file name |
| jstring fileName = |
| CallObjectMethod(jenv, stackTraceElement, getFileNameId); |
| |
| if (ExceptionCheck(jenv)) |
| return; |
| |
| // if it's known source ... |
| if (fileName) { |
| // gets line number and prints it after file name |
| const char *fileNameChars = |
| GetStringUTFChars(jenv, fileName, NULL); |
| jint sourceLineNumber = |
| CallIntMethod(jenv, stackTraceElement, getLineNumberId); |
| if (ExceptionCheck(jenv)) |
| return; |
| fprintf(f, " (%s:", fileNameChars); |
| fprintf(f, " %d)", sourceLineNumber); |
| } |
| // if it's unknown source |
| else { |
| jboolean isNative = |
| CallBooleanMethod(jenv, stackTraceElement, isNativeMethodId); |
| if (ExceptionCheck(jenv)) |
| return; |
| |
| // if it's native |
| if (isNative) { |
| fprintf(f, " (Native Method)"); |
| } |
| // or not |
| else { |
| fprintf(f, " (Unknown Source)"); |
| } |
| } |
| |
| // prints end of stack trace line |
| fprintf(f, "\n"); |
| } |
| |
| // gets caused exception |
| jmethodID getCauseId = |
| GetMethodID(jenv, throwableClazz, "getCause", |
| "()Ljava/lang/Throwable;"); |
| jobject causedExc = CallObjectMethod(jenv, exc, getCauseId); |
| if (ExceptionCheck(jenv)) |
| return; |
| |
| if (causedExc) { |
| // if there is caused exception ... |
| tmn_suspend_disable(); // -----------------vvv |
| bool same_exception = false; |
| if (causedExc->object == exc->object) |
| same_exception = true; |
| tmn_suspend_enable(); // ------------------^^^ |
| if (!same_exception) { |
| // tries to print it |
| fprintf(f, "caused by\n"); |
| exn_jni_print_stack_trace(f, causedExc); |
| } |
| } |
| |
| assert(hythread_is_suspend_enabled()); |
| } |
| |
| inline void exn_native_print_stack_trace(FILE * f, ManagedObject * exn) |
| { |
| //Afremov Pavel 20050119 Should be changed when classpath will raplaced by DRL |
| assert(hythread_is_suspend_enabled()); |
| assert(gid_throwable_traceinfo); |
| |
| // ? 20030428: This code should be elsewhere! |
| unsigned field_offset = ((Field *) gid_throwable_traceinfo)->get_offset(); |
| ManagedObject **field_addr = |
| (ManagedObject **) ((U_8*) exn + field_offset); |
| Vector_Handle stack_trace = |
| (Vector_Handle) get_raw_reference_pointer(field_addr); |
| if (stack_trace) { |
| #if defined (__INTEL_COMPILER) |
| #pragma warning( push ) |
| #pragma warning (disable:1683) // to get rid of remark #1683: explicit conversion of a 64-bit integral type to a smaller integral type |
| #endif |
| unsigned depth = |
| (unsigned) *get_vector_element_address_int64(stack_trace, 0); |
| |
| #if defined (__INTEL_COMPILER) |
| #pragma warning( pop ) |
| #endif |
| |
| StackTraceFrame *stf = |
| (StackTraceFrame *) get_vector_element_address_int64(stack_trace, |
| 1); |
| |
| ExpandableMemBlock buf; |
| for (unsigned i = 0; i < depth; i++) |
| st_print_frame(&buf, stf + i); |
| fprintf(f, "%s", buf.toString()); |
| } |
| } |
| |
| // prints stack trace using 3 ways: via java, via jni, and native |
| void exn_print_stack_trace(FILE * f, jthrowable exc) |
| { |
| assert(hythread_is_suspend_enabled()); |
| // saves curent thread exception and clear to allow java to work |
| jthrowable cte = exn_get(); |
| /* |
| Afremov Pavel 20050120 |
| FIXME:Don't work under JIT, Fix requred |
| // 1 way -> tries to print stacktrace via java |
| exn_java_print_stack_trace(f, exn); |
| |
| // if everything ok ... |
| if (!exn_raised()) |
| { |
| // restores curent thread exception and returns |
| exn_raise_object(cte); |
| return; |
| } |
| */ |
| // clears exception to allow java to work |
| exn_clear(); |
| |
| // 2 way -> tries to print using jni access to class method |
| exn_jni_print_stack_trace(f, exc); |
| |
| // if everything OK ... |
| if (!exn_raised()) |
| { |
| // restores curent thread exception and returns |
| exn_raise_object(cte); |
| return; |
| } |
| |
| // 3 way -> last |
| tmn_suspend_disable(); |
| |
| ManagedObject *exn = exc->object; |
| exn_native_print_stack_trace(f, exn); |
| |
| tmn_suspend_enable(); |
| |
| fflush(f); |
| |
| // restore curent thread exception |
| exn_raise_object(cte); |
| |
| } |
| |
| |
| ////////////////////////////////////////////////////////////////////////// |
| // Uncaught Exceptions |
| |
| void print_uncaught_exception_message(FILE * f, const char *context_message, |
| jthrowable exc) |
| { |
| assert(hythread_is_suspend_enabled()); |
| |
| tmn_suspend_disable(); |
| fprintf(f, "** During %s uncaught exception: %s\n", context_message, |
| exc->object->vt()->clss->get_name()->bytes); |
| tmn_suspend_enable(); |
| |
| exn_print_stack_trace(f, exc); |
| } |