This introduces vm_throwable_print_stack_trace() which generates a string which is identical to the one that would be printed if Throwable.printStackTrace() method is executed.
Signed-off-by: Tomek Grabiec <tgrab...@gmail.com> --- include/vm/preload.h | 9 ++ vm/preload.c | 64 +++++++++++++++- vm/stack-trace.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 278 insertions(+), 4 deletions(-) diff --git a/include/vm/preload.h b/include/vm/preload.h index 646a68c..584ae9f 100644 --- a/include/vm/preload.h +++ b/include/vm/preload.h @@ -41,6 +41,15 @@ extern struct vm_field *vm_java_lang_VMThrowable_vmdata; extern struct vm_method *vm_java_util_Properties_setProperty; extern struct vm_method *vm_java_lang_Throwable_initCause; +extern struct vm_method *vm_java_lang_Throwable_getCause; +extern struct vm_method *vm_java_lang_Throwable_toString; +extern struct vm_method *vm_java_lang_Throwable_getStackTrace; +extern struct vm_method *vm_java_lang_StackTraceElement_getFileName; +extern struct vm_method *vm_java_lang_StackTraceElement_getClassName; +extern struct vm_method *vm_java_lang_StackTraceElement_getMethodName; +extern struct vm_method *vm_java_lang_StackTraceElement_getLineNumber; +extern struct vm_method *vm_java_lang_StackTraceElement_isNativeMethod; +extern struct vm_method *vm_java_lang_StackTraceElement_equals; int preload_vm_classes(void); diff --git a/vm/preload.c b/vm/preload.c index 9f8de3f..4d2c5f6 100644 --- a/vm/preload.c +++ b/vm/preload.c @@ -136,6 +136,15 @@ struct method_preload_entry { struct vm_method *vm_java_util_Properties_setProperty; struct vm_method *vm_java_lang_Throwable_initCause; +struct vm_method *vm_java_lang_Throwable_getCause; +struct vm_method *vm_java_lang_Throwable_toString; +struct vm_method *vm_java_lang_Throwable_getStackTrace; +struct vm_method *vm_java_lang_StackTraceElement_getFileName; +struct vm_method *vm_java_lang_StackTraceElement_getClassName; +struct vm_method *vm_java_lang_StackTraceElement_getMethodName; +struct vm_method *vm_java_lang_StackTraceElement_getLineNumber; +struct vm_method *vm_java_lang_StackTraceElement_isNativeMethod; +struct vm_method *vm_java_lang_StackTraceElement_equals; static const struct method_preload_entry method_preload_entries[] = { { @@ -150,7 +159,60 @@ static const struct method_preload_entry method_preload_entries[] = { "(Ljava/lang/Throwable;)Ljava/lang/Throwable;", &vm_java_lang_Throwable_initCause, }, - + { + &vm_java_lang_Throwable, + "getCause", + "()Ljava/lang/Throwable;", + &vm_java_lang_Throwable_getCause, + }, + { + &vm_java_lang_Throwable, + "getStackTrace", + "()[Ljava/lang/StackTraceElement;", + &vm_java_lang_Throwable_getStackTrace, + }, + { + &vm_java_lang_Throwable, + "toString", + "()Ljava/lang/String;", + &vm_java_lang_Throwable_toString, + }, + { + &vm_java_lang_StackTraceElement, + "getFileName", + "()Ljava/lang/String;", + &vm_java_lang_StackTraceElement_getFileName, + }, + { + &vm_java_lang_StackTraceElement, + "getClassName", + "()Ljava/lang/String;", + &vm_java_lang_StackTraceElement_getClassName, + }, + { + &vm_java_lang_StackTraceElement, + "getMethodName", + "()Ljava/lang/String;", + &vm_java_lang_StackTraceElement_getMethodName, + }, + { + &vm_java_lang_StackTraceElement, + "getLineNumber", + "()I", + &vm_java_lang_StackTraceElement_getLineNumber, + }, + { + &vm_java_lang_StackTraceElement, + "isNativeMethod", + "()Z", + &vm_java_lang_StackTraceElement_isNativeMethod, + }, + { + &vm_java_lang_StackTraceElement, + "equals", + "(Ljava/lang/Object;)Z", + &vm_java_lang_StackTraceElement_equals, + }, }; int preload_vm_classes(void) diff --git a/vm/stack-trace.c b/vm/stack-trace.c index d80582f..d5cf437 100644 --- a/vm/stack-trace.c +++ b/vm/stack-trace.c @@ -382,7 +382,185 @@ native_vmthrowable_get_stack_trace(struct vm_object *this, return result; } -void vm_print_exception(struct vm_object *exception) +typedef struct vm_object *(*vm_java_lang_Throwable_toString_fn) + (struct vm_object *); +typedef struct vm_object *(*vm_java_lang_Throwable_getCause_fn) + (struct vm_object *); +typedef struct vm_object *(*vm_java_lang_Throwable_getStackTrace_fn) + (struct vm_object *); +typedef struct vm_object *(*vm_java_lang_StackTraceElement_getFileName_fn) + (struct vm_object *); +typedef struct vm_object *(*vm_java_lang_StackTraceElement_getClassName_fn) + (struct vm_object *); +typedef struct vm_object *(*vm_java_lang_StackTraceElement_getMethodName_fn) + (struct vm_object *); +typedef int (*vm_java_lang_StackTraceElement_getLineNumber_fn) + (struct vm_object *); +typedef bool (*vm_java_lang_StackTraceElement_isNativeMethod_fn) + (struct vm_object *); +typedef bool (*vm_java_lang_StackTraceElement_equals_fn) + (struct vm_object *, struct vm_object *); + +static void +vm_throwable_to_string(struct vm_object *this, struct string *str) +{ + vm_java_lang_Throwable_toString_fn toString = + vm_method_trampoline_ptr(vm_java_lang_Throwable_toString); + struct vm_object *string_obj; + + string_obj = toString(this); + if (!string_obj) + return; + + char *cstr = vm_string_to_cstr(string_obj); + str_append(str, cstr); + free(cstr); +} + +static void vm_stack_trace_element_to_string(struct vm_object *elem, + struct string *str) +{ + vm_java_lang_StackTraceElement_getFileName_fn getFileName; + vm_java_lang_StackTraceElement_getClassName_fn getClassName; + vm_java_lang_StackTraceElement_getMethodName_fn getMethodName; + vm_java_lang_StackTraceElement_getLineNumber_fn getLineNumber; + vm_java_lang_StackTraceElement_isNativeMethod_fn isNativeMethod; + struct vm_object *method_name; + struct vm_object *file_name; + struct vm_object *class_name; + char *method_name_str; + char *file_name_str; + char *class_name_str; + int line_number; + bool is_native; + + getMethodName = vm_method_trampoline_ptr( + vm_java_lang_StackTraceElement_getMethodName); + getFileName = vm_method_trampoline_ptr( + vm_java_lang_StackTraceElement_getFileName); + getClassName = vm_method_trampoline_ptr( + vm_java_lang_StackTraceElement_getClassName); + getLineNumber = vm_method_trampoline_ptr( + vm_java_lang_StackTraceElement_getLineNumber); + isNativeMethod = vm_method_trampoline_ptr( + vm_java_lang_StackTraceElement_isNativeMethod); + + file_name = getFileName(elem); + line_number = getLineNumber(elem); + class_name = getClassName(elem); + method_name = getMethodName(elem); + is_native = isNativeMethod(elem); + + method_name_str = vm_string_to_cstr(method_name); + file_name_str = vm_string_to_cstr(file_name); + class_name_str = vm_string_to_cstr(class_name); + + if (class_name_str) { + str_append(str, class_name_str); + if (method_name_str) + str_append(str, "."); + } + + if (method_name_str) + str_append(str, method_name_str); + + str_append(str, "("); + + if (file_name_str) { + str_append(str, file_name_str); + } else { + if (is_native) + str_append(str, "Native Method"); + else + str_append(str, "Unknown Source"); + } + + if (line_number >= 0) + str_append(str, ":%d", line_number); + + str_append(str, ")"); +} + +static void +vm_throwable_stack_trace(struct vm_object *this, struct string *str, + struct vm_object *stack, int equal) +{ + vm_throwable_to_string(this, str); + str_append(str, "\n"); + + if (stack == NULL || stack->array_length == 0) { + str_append(str, " <<No stacktrace available>>\n"); + return; + } + + for (int i = 0; i < stack->array_length - equal; i++) { + struct vm_object *elem = array_get_field_ptr(stack, i); + + str_append(str, " at "); + if (!elem) + str_append(str, "<<Unknown>>"); + else + vm_stack_trace_element_to_string(elem, str); + + str_append(str, "\n"); + } + + if (equal > 0) + str_append(str, " ...%d more\n", equal); +} + + +static void +vm_throwable_print_stack_trace(struct vm_object *this, struct string *str) +{ + vm_java_lang_Throwable_getCause_fn getCause; + vm_java_lang_Throwable_getStackTrace_fn getStackTrace; + vm_java_lang_StackTraceElement_equals_fn ste_equals; + struct vm_object *stack; + + getCause = + vm_method_trampoline_ptr(vm_java_lang_Throwable_getCause); + getStackTrace = + vm_method_trampoline_ptr(vm_java_lang_Throwable_getStackTrace); + ste_equals = + vm_method_trampoline_ptr(vm_java_lang_StackTraceElement_equals); + + stack = getStackTrace(this); + vm_throwable_stack_trace(this, str, stack, 0); + + struct vm_object *cause = this; + while ((cause = getCause(cause))) { + struct vm_object *p_stack; + + str_append(str, "Caused by: "); + + p_stack = stack; + stack = getStackTrace(cause); + + if (p_stack == NULL || p_stack->array_length == 0) { + vm_throwable_stack_trace(cause, str, stack, 0); + continue; + } + + int equal = 0; + int frame = stack->array_length - 1; + int p_frame = p_stack->array_length - 1; + + while (frame > 0 && p_frame > 0) { + if (!ste_equals(array_get_field_ptr(stack, frame), + array_get_field_ptr(p_stack, p_frame))) + break; + + equal++; + frame--; + p_frame--; + } + + vm_throwable_stack_trace(cause, str, stack, equal); + } +} + +static void vm_print_exception_description(struct vm_object *exception) { struct vm_object *message_obj; @@ -400,7 +578,32 @@ void vm_print_exception(struct vm_object *exception) free(msg_str); } - fprintf(stderr, "\n <<No stack trace available>>\n"); + fprintf(stderr, "\n <<No stacktrace available>>\n"); +} + +void vm_print_exception(struct vm_object *exception) +{ + struct string *str; + + str = alloc_str(); + if (!str) + goto error; + + /* TODO: print correct thread name */ + str_append(str, "Exception in thread \"main\" "); + + vm_throwable_print_stack_trace(exception, str); + if (exception_occurred()) + goto error; + + fprintf(stderr, str->value); + free_str(str); + + return; +error: + warn("unable to print exception"); + if (str) + free_str(str); - NOT_IMPLEMENTED; + vm_print_exception_description(exception); } -- 1.6.0.6 ------------------------------------------------------------------------------ _______________________________________________ Jatovm-devel mailing list Jatovm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jatovm-devel