OpenJDK / amber / amber
changeset 37064:5c82fa70d313
8150778: Reduce Throwable.getStackTrace() calls to the JVM
Summary: replace JVM_GetStackTraceDepth and JVM_GetStackTraceElement, with JVM_GetStackTraceElements that gets all the elements in the StackTraceElement[]
Reviewed-by: shade, mchung, dholmes, hseigel
author | coleenp |
---|---|
date | Thu, 10 Mar 2016 13:43:47 -0500 |
parents | c9d5a3727011 |
children | f4ebb8b35388 |
files | hotspot/make/share/makefiles/mapfile-vers hotspot/src/share/vm/classfile/javaClasses.cpp hotspot/src/share/vm/classfile/javaClasses.hpp hotspot/src/share/vm/classfile/vmSymbols.hpp hotspot/src/share/vm/logging/logTag.hpp hotspot/src/share/vm/prims/jvm.cpp hotspot/src/share/vm/prims/jvm.h hotspot/test/runtime/Throwable/StackTraceLogging.java hotspot/test/runtime/Throwable/TestThrowable.java |
diffstat | 9 files changed, 317 insertions(+), 168 deletions(-) [+] |
line wrap: on
line diff
--- a/hotspot/make/share/makefiles/mapfile-vers Thu Mar 10 14:15:15 2016 +0100 +++ b/hotspot/make/share/makefiles/mapfile-vers Thu Mar 10 13:43:47 2016 -0500 @@ -109,8 +109,7 @@ JVM_GetPrimitiveArrayElement; JVM_GetProtectionDomain; JVM_GetStackAccessControlContext; - JVM_GetStackTraceDepth; - JVM_GetStackTraceElement; + JVM_GetStackTraceElements; JVM_GetSystemPackage; JVM_GetSystemPackages; JVM_GetTemporaryDirectory;
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp Thu Mar 10 14:15:15 2016 +0100 +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Thu Mar 10 13:43:47 2016 -0500 @@ -1439,6 +1439,12 @@ compute_offset(_ngroups_offset, k, vmSymbols::ngroups_name(), vmSymbols::int_signature()); } + +void java_lang_Throwable::compute_offsets() { + Klass* k = SystemDictionary::Throwable_klass(); + compute_offset(depth_offset, k, vmSymbols::depth_name(), vmSymbols::int_signature()); +} + oop java_lang_Throwable::unassigned_stacktrace() { InstanceKlass* ik = SystemDictionary::Throwable_klass(); address addr = ik->static_field_addr(static_unassigned_stacktrace_offset); @@ -1458,11 +1464,13 @@ throwable->release_obj_field_put(backtrace_offset, value); } - -oop java_lang_Throwable::message(oop throwable) { - return throwable->obj_field(detailMessage_offset); -} - +int java_lang_Throwable::depth(oop throwable) { + return throwable->int_field(depth_offset); +} + +void java_lang_Throwable::set_depth(oop throwable, int value) { + throwable->int_field_put(depth_offset, value); +} oop java_lang_Throwable::message(Handle throwable) { return throwable->obj_field(detailMessage_offset); @@ -1512,10 +1520,12 @@ return method != NULL && (method->constants()->version() == version); } + // This class provides a simple wrapper over the internal structure of // exception backtrace to insulate users of the backtrace from needing // to know what it looks like. class BacktraceBuilder: public StackObj { + friend class BacktraceIterator; private: Handle _backtrace; objArrayOop _head; @@ -1526,8 +1536,6 @@ int _index; NoSafepointVerifier _nsv; - public: - enum { trace_methods_offset = java_lang_Throwable::trace_methods_offset, trace_bcis_offset = java_lang_Throwable::trace_bcis_offset, @@ -1560,6 +1568,8 @@ return cprefs; } + public: + // constructor for new backtrace BacktraceBuilder(TRAPS): _methods(NULL), _bcis(NULL), _head(NULL), _mirrors(NULL), _cprefs(NULL) { expand(CHECK); @@ -1645,9 +1655,68 @@ }; +struct BacktraceElement : public StackObj { + int _method_id; + int _bci; + int _version; + int _cpref; + Handle _mirror; + BacktraceElement(Handle mirror, int mid, int version, int bci, int cpref) : + _mirror(mirror), _method_id(mid), _version(version), _bci(bci), _cpref(cpref) {} +}; + +class BacktraceIterator : public StackObj { + int _index; + objArrayHandle _result; + objArrayHandle _mirrors; + typeArrayHandle _methods; + typeArrayHandle _bcis; + typeArrayHandle _cprefs; + + void init(objArrayHandle result, Thread* thread) { + // Get method id, bci, version and mirror from chunk + _result = result; + if (_result.not_null()) { + _methods = typeArrayHandle(thread, BacktraceBuilder::get_methods(_result)); + _bcis = typeArrayHandle(thread, BacktraceBuilder::get_bcis(_result)); + _mirrors = objArrayHandle(thread, BacktraceBuilder::get_mirrors(_result)); + _cprefs = typeArrayHandle(thread, BacktraceBuilder::get_cprefs(_result)); + _index = 0; + } + } + public: + BacktraceIterator(objArrayHandle result, Thread* thread) { + init(result, thread); + assert(_methods->length() == java_lang_Throwable::trace_chunk_size, "lengths don't match"); + } + + BacktraceElement next(Thread* thread) { + BacktraceElement e (Handle(thread, _mirrors->obj_at(_index)), + _methods->short_at(_index), + Backtrace::version_at(_bcis->int_at(_index)), + Backtrace::bci_at(_bcis->int_at(_index)), + _cprefs->short_at(_index)); + _index++; + + if (_index >= java_lang_Throwable::trace_chunk_size) { + int next_offset = java_lang_Throwable::trace_next_offset; + // Get next chunk + objArrayHandle result (thread, objArrayOop(_result->obj_at(next_offset))); + init(result, thread); + } + return e; + } + + bool repeat() { + return _result.not_null() && _mirrors->obj_at(_index) != NULL; + } +}; + + // Print stack trace element to resource allocated buffer -char* java_lang_Throwable::print_stack_element_to_buffer(Handle mirror, - int method_id, int version, int bci, int cpref) { +static void print_stack_element_to_stream(outputStream* st, Handle mirror, int method_id, + int version, int bci, int cpref) { + ResourceMark rm; // Get strings and string lengths InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror())); @@ -1698,26 +1767,16 @@ } } - return buf; -} - -void java_lang_Throwable::print_stack_element(outputStream *st, Handle mirror, - int method_id, int version, int bci, int cpref) { - ResourceMark rm; - char* buf = print_stack_element_to_buffer(mirror, method_id, version, bci, cpref); st->print_cr("%s", buf); } + void java_lang_Throwable::print_stack_element(outputStream *st, const methodHandle& method, int bci) { Handle mirror = method->method_holder()->java_mirror(); int method_id = method->orig_method_idnum(); int version = method->constants()->version(); int cpref = method->name_index(); - print_stack_element(st, mirror, method_id, version, bci, cpref); -} - -const char* java_lang_Throwable::no_stack_trace_message() { - return "\t<<no stack trace available>>"; + print_stack_element_to_stream(st, mirror, method_id, version, bci, cpref); } /** @@ -1734,32 +1793,17 @@ while (throwable.not_null()) { objArrayHandle result (THREAD, objArrayOop(backtrace(throwable()))); if (result.is_null()) { - st->print_raw_cr(no_stack_trace_message()); + st->print_raw_cr("\t<<no stack trace available>>"); return; } - - while (result.not_null()) { - // Get method id, bci, version and mirror from chunk - typeArrayHandle methods (THREAD, BacktraceBuilder::get_methods(result)); - typeArrayHandle bcis (THREAD, BacktraceBuilder::get_bcis(result)); - objArrayHandle mirrors (THREAD, BacktraceBuilder::get_mirrors(result)); - typeArrayHandle cprefs (THREAD, BacktraceBuilder::get_cprefs(result)); - - int length = methods()->length(); - for (int index = 0; index < length; index++) { - Handle mirror(THREAD, mirrors->obj_at(index)); - // NULL mirror means end of stack trace - if (mirror.is_null()) goto handle_cause; - int method = methods->short_at(index); - int version = Backtrace::version_at(bcis->int_at(index)); - int bci = Backtrace::bci_at(bcis->int_at(index)); - int cpref = cprefs->short_at(index); - print_stack_element(st, mirror, method, version, bci, cpref); - } - result = objArrayHandle(THREAD, objArrayOop(result->obj_at(trace_next_offset))); + BacktraceIterator iter(result, THREAD); + + while (iter.repeat()) { + BacktraceElement bte = iter.next(THREAD); + print_stack_element_to_stream(st, bte._mirror, bte._method_id, bte._version, bte._bci, bte._cpref); } - handle_cause: { + // Call getCause() which doesn't necessarily return the _cause field. EXCEPTION_MARK; JavaValue cause(T_OBJECT); JavaCalls::call_virtual(&cause, @@ -1811,6 +1855,7 @@ int max_depth = MaxJavaStackTraceDepth; JavaThread* thread = (JavaThread*)THREAD; + BacktraceBuilder bt(CHECK); // If there is no Java frame just return the method that was being called @@ -1818,6 +1863,8 @@ if (!thread->has_last_Java_frame()) { if (max_depth >= 1 && method() != NULL) { bt.push(method(), 0, CHECK); + log_info(stacktrace)("%s, %d", throwable->klass()->external_name(), 1); + set_depth(throwable(), 1); set_backtrace(throwable(), bt.backtrace()); } return; @@ -1925,8 +1972,11 @@ total_count++; } + log_info(stacktrace)("%s, %d", throwable->klass()->external_name(), total_count); + // Put completed stack trace into throwable object set_backtrace(throwable(), bt.backtrace()); + set_depth(throwable(), total_count); } void java_lang_Throwable::fill_in_stack_trace(Handle throwable, const methodHandle& method) { @@ -1980,94 +2030,60 @@ // methods as preallocated errors aren't created by "java" code. // fill in as much stack trace as possible - typeArrayOop methods = BacktraceBuilder::get_methods(backtrace); - int max_chunks = MIN2(methods->length(), (int)MaxJavaStackTraceDepth); int chunk_count = 0; - for (;!st.at_end(); st.next()) { bt.push(st.method(), st.bci(), CHECK); chunk_count++; // Bail-out for deep stacks - if (chunk_count >= max_chunks) break; + if (chunk_count >= trace_chunk_size) break; } + set_depth(throwable(), chunk_count); + log_info(stacktrace)("%s, %d", throwable->klass()->external_name(), chunk_count); // We support the Throwable immutability protocol defined for Java 7. java_lang_Throwable::set_stacktrace(throwable(), java_lang_Throwable::unassigned_stacktrace()); assert(java_lang_Throwable::unassigned_stacktrace() != NULL, "not initialized"); } - -int java_lang_Throwable::get_stack_trace_depth(oop throwable, TRAPS) { - if (throwable == NULL) { - THROW_0(vmSymbols::java_lang_NullPointerException()); +void java_lang_Throwable::get_stack_trace_elements(Handle throwable, + objArrayHandle stack_trace_array_h, TRAPS) { + + if (throwable.is_null() || stack_trace_array_h.is_null()) { + THROW(vmSymbols::java_lang_NullPointerException()); } - objArrayOop chunk = objArrayOop(backtrace(throwable)); - int depth = 0; - if (chunk != NULL) { - // Iterate over chunks and count full ones - while (true) { - objArrayOop next = objArrayOop(chunk->obj_at(trace_next_offset)); - if (next == NULL) break; - depth += trace_chunk_size; - chunk = next; + + assert(stack_trace_array_h->is_objArray(), "Stack trace array should be an array of StackTraceElenent"); + + if (stack_trace_array_h->length() != depth(throwable())) { + THROW(vmSymbols::java_lang_IndexOutOfBoundsException()); + } + + objArrayHandle result(THREAD, objArrayOop(backtrace(throwable()))); + BacktraceIterator iter(result, THREAD); + + int index = 0; + while (iter.repeat()) { + BacktraceElement bte = iter.next(THREAD); + + Handle stack_trace_element(THREAD, stack_trace_array_h->obj_at(index++)); + + if (stack_trace_element.is_null()) { + THROW(vmSymbols::java_lang_NullPointerException()); } - assert(chunk != NULL && chunk->obj_at(trace_next_offset) == NULL, "sanity check"); - // Count element in remaining partial chunk. NULL value for mirror - // marks the end of the stack trace elements that are saved. - objArrayOop mirrors = BacktraceBuilder::get_mirrors(chunk); - assert(mirrors != NULL, "sanity check"); - for (int i = 0; i < mirrors->length(); i++) { - if (mirrors->obj_at(i) == NULL) break; - depth++; - } + + InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(bte._mirror())); + methodHandle method (THREAD, holder->method_with_orig_idnum(bte._method_id, bte._version)); + + java_lang_StackTraceElement::fill_in(stack_trace_element, holder, + method, + bte._version, + bte._bci, + bte._cpref, CHECK); } - return depth; -} - - -oop java_lang_Throwable::get_stack_trace_element(oop throwable, int index, TRAPS) { - if (throwable == NULL) { - THROW_0(vmSymbols::java_lang_NullPointerException()); - } - if (index < 0) { - THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL); - } - // Compute how many chunks to skip and index into actual chunk - objArrayOop chunk = objArrayOop(backtrace(throwable)); - int skip_chunks = index / trace_chunk_size; - int chunk_index = index % trace_chunk_size; - while (chunk != NULL && skip_chunks > 0) { - chunk = objArrayOop(chunk->obj_at(trace_next_offset)); - skip_chunks--; - } - if (chunk == NULL) { - THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL); - } - // Get method id, bci, version, mirror and cpref from chunk - typeArrayOop methods = BacktraceBuilder::get_methods(chunk); - typeArrayOop bcis = BacktraceBuilder::get_bcis(chunk); - objArrayOop mirrors = BacktraceBuilder::get_mirrors(chunk); - typeArrayOop cprefs = BacktraceBuilder::get_cprefs(chunk); - - assert(methods != NULL && bcis != NULL && mirrors != NULL, "sanity check"); - - int method = methods->short_at(chunk_index); - int version = Backtrace::version_at(bcis->int_at(chunk_index)); - int bci = Backtrace::bci_at(bcis->int_at(chunk_index)); - int cpref = cprefs->short_at(chunk_index); - Handle mirror(THREAD, mirrors->obj_at(chunk_index)); - - // Chunk can be partial full - if (mirror.is_null()) { - THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL); - } - oop element = java_lang_StackTraceElement::create(mirror, method, version, bci, cpref, CHECK_0); - return element; -} - -oop java_lang_StackTraceElement::create(Handle mirror, int method_id, - int version, int bci, int cpref, TRAPS) { +} + +oop java_lang_StackTraceElement::create(const methodHandle& method, int bci, TRAPS) { // Allocate java.lang.StackTraceElement instance Klass* k = SystemDictionary::StackTraceElement_klass(); assert(k != NULL, "must be loaded in 1.4+"); @@ -2078,23 +2094,31 @@ Handle element = ik->allocate_instance_handle(CHECK_0); + int cpref = method->name_index(); + int version = method->constants()->version(); + fill_in(element, method->method_holder(), method, version, bci, cpref, CHECK_0); + return element(); +} + +void java_lang_StackTraceElement::fill_in(Handle element, + InstanceKlass* holder, const methodHandle& method, + int version, int bci, int cpref, TRAPS) { + assert(element->is_a(SystemDictionary::StackTraceElement_klass()), "sanity check"); + // Fill in class name ResourceMark rm(THREAD); - InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror())); const char* str = holder->external_name(); - oop classname = StringTable::intern((char*) str, CHECK_0); + oop classname = StringTable::intern((char*) str, CHECK); java_lang_StackTraceElement::set_declaringClass(element(), classname); - Method* method = holder->method_with_orig_idnum(method_id, version); - // The method can be NULL if the requested class version is gone - Symbol* sym = (method != NULL) ? method->name() : holder->constants()->symbol_at(cpref); + Symbol* sym = !method.is_null() ? method->name() : holder->constants()->symbol_at(cpref); // Fill in method name - oop methodname = StringTable::intern(sym, CHECK_0); + oop methodname = StringTable::intern(sym, CHECK); java_lang_StackTraceElement::set_methodName(element(), methodname); - if (!version_matches(method, version)) { + if (!version_matches(method(), version)) { // The method was redefined, accurate line number information isn't available java_lang_StackTraceElement::set_fileName(element(), NULL); java_lang_StackTraceElement::set_lineNumber(element(), -1); @@ -2103,20 +2127,12 @@ Symbol* source = Backtrace::get_source_file_name(holder, version); if (ShowHiddenFrames && source == NULL) source = vmSymbols::unknown_class_name(); - oop filename = StringTable::intern(source, CHECK_0); + oop filename = StringTable::intern(source, CHECK); java_lang_StackTraceElement::set_fileName(element(), filename); int line_number = Backtrace::get_line_number(method, bci); java_lang_StackTraceElement::set_lineNumber(element(), line_number); } - return element(); -} - -oop java_lang_StackTraceElement::create(const methodHandle& method, int bci, TRAPS) { - Handle mirror (THREAD, method->method_holder()->java_mirror()); - int method_id = method->orig_method_idnum(); - int cpref = method->name_index(); - return create(mirror, method_id, method->constants()->version(), bci, cpref, THREAD); } Method* java_lang_StackFrameInfo::get_method(Handle stackFrame, InstanceKlass* holder, TRAPS) { @@ -3477,8 +3493,8 @@ GrowableArray<Klass*>* java_lang_Class::_fixup_mirror_list = NULL; int java_lang_Throwable::backtrace_offset; int java_lang_Throwable::detailMessage_offset; -int java_lang_Throwable::cause_offset; int java_lang_Throwable::stackTrace_offset; +int java_lang_Throwable::depth_offset; int java_lang_Throwable::static_unassigned_stacktrace_offset; int java_lang_reflect_AccessibleObject::override_offset; int java_lang_reflect_Method::clazz_offset; @@ -3679,7 +3695,6 @@ // Throwable Class java_lang_Throwable::backtrace_offset = java_lang_Throwable::hc_backtrace_offset * x + header; java_lang_Throwable::detailMessage_offset = java_lang_Throwable::hc_detailMessage_offset * x + header; - java_lang_Throwable::cause_offset = java_lang_Throwable::hc_cause_offset * x + header; java_lang_Throwable::stackTrace_offset = java_lang_Throwable::hc_stackTrace_offset * x + header; java_lang_Throwable::static_unassigned_stacktrace_offset = java_lang_Throwable::hc_static_unassigned_stacktrace_offset * x; @@ -3730,6 +3745,7 @@ void JavaClasses::compute_offsets() { // java_lang_Class::compute_offsets was called earlier in bootstrap java_lang_ClassLoader::compute_offsets(); + java_lang_Throwable::compute_offsets(); java_lang_Thread::compute_offsets(); java_lang_ThreadGroup::compute_offsets(); java_lang_invoke_MethodHandle::compute_offsets(); @@ -3883,8 +3899,8 @@ CHECK_OFFSET("java/lang/Throwable", java_lang_Throwable, backtrace, "Ljava/lang/Object;"); CHECK_OFFSET("java/lang/Throwable", java_lang_Throwable, detailMessage, "Ljava/lang/String;"); - CHECK_OFFSET("java/lang/Throwable", java_lang_Throwable, cause, "Ljava/lang/Throwable;"); CHECK_OFFSET("java/lang/Throwable", java_lang_Throwable, stackTrace, "[Ljava/lang/StackTraceElement;"); + CHECK_OFFSET("java/lang/Throwable", java_lang_Throwable, depth, "I"); // Boxed primitive objects (java_lang_boxing_object)
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Thu Mar 10 14:15:15 2016 +0100 +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Thu Mar 10 13:43:47 2016 -0500 @@ -440,6 +440,7 @@ class java_lang_Throwable: AllStatic { friend class BacktraceBuilder; + friend class BacktraceIterator; private: // Offsets @@ -465,16 +466,12 @@ static int backtrace_offset; static int detailMessage_offset; - static int cause_offset; static int stackTrace_offset; + static int depth_offset; static int static_unassigned_stacktrace_offset; - // Printing - static char* print_stack_element_to_buffer(Handle mirror, int method, int version, int bci, int cpref); // StackTrace (programmatic access, new since 1.4) static void clear_stacktrace(oop throwable); - // No stack trace available - static const char* no_stack_trace_message(); // Stacktrace (post JDK 1.7.0 to allow immutability protocol to be followed) static void set_stacktrace(oop throwable, oop st_element_array); static oop unassigned_stacktrace(); @@ -483,19 +480,20 @@ // Backtrace static oop backtrace(oop throwable); static void set_backtrace(oop throwable, oop value); + static int depth(oop throwable); + static void set_depth(oop throwable, int value); // Needed by JVMTI to filter out this internal field. static int get_backtrace_offset() { return backtrace_offset;} static int get_detailMessage_offset() { return detailMessage_offset;} // Message - static oop message(oop throwable); static oop message(Handle throwable); static void set_message(oop throwable, oop value); static Symbol* detail_message(oop throwable); - static void print_stack_element(outputStream *st, Handle mirror, int method, - int version, int bci, int cpref); static void print_stack_element(outputStream *st, const methodHandle& method, int bci); static void print_stack_usage(Handle stream); + static void compute_offsets(); + // Allocate space for backtrace (created but stack trace not filled in) static void allocate_backtrace(Handle throwable, TRAPS); // Fill in current stack trace for throwable with preallocated backtrace (no GC) @@ -504,8 +502,7 @@ static void fill_in_stack_trace(Handle throwable, const methodHandle& method, TRAPS); static void fill_in_stack_trace(Handle throwable, const methodHandle& method = methodHandle()); // Programmatic access to stack trace - static oop get_stack_trace_element(oop throwable, int index, TRAPS); - static int get_stack_trace_depth(oop throwable, TRAPS); + static void get_stack_trace_elements(Handle throwable, objArrayHandle stack_trace, TRAPS); // Printing static void print(Handle throwable, outputStream* st); static void print_stack_trace(Handle throwable, outputStream* st); @@ -1277,17 +1274,19 @@ static int fileName_offset; static int lineNumber_offset; - public: // Setters static void set_declaringClass(oop element, oop value); static void set_methodName(oop element, oop value); static void set_fileName(oop element, oop value); static void set_lineNumber(oop element, int value); + public: // Create an instance of StackTraceElement - static oop create(Handle mirror, int method, int version, int bci, int cpref, TRAPS); static oop create(const methodHandle& method, int bci, TRAPS); + static void fill_in(Handle element, InstanceKlass* holder, const methodHandle& method, + int version, int bci, int cpref, TRAPS); + // Debugging friend class JavaClasses; };
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp Thu Mar 10 14:15:15 2016 +0100 +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp Thu Mar 10 13:43:47 2016 -0500 @@ -376,6 +376,7 @@ template(fillInStackTrace_name, "fillInStackTrace") \ template(getCause_name, "getCause") \ template(initCause_name, "initCause") \ + template(depth_name, "depth") \ template(setProperty_name, "setProperty") \ template(getProperty_name, "getProperty") \ template(context_name, "context") \
--- a/hotspot/src/share/vm/logging/logTag.hpp Thu Mar 10 14:15:15 2016 +0100 +++ b/hotspot/src/share/vm/logging/logTag.hpp Thu Mar 10 13:43:47 2016 -0500 @@ -76,6 +76,7 @@ LOG_TAG(safepointcleanup) \ LOG_TAG(scavenge) \ LOG_TAG(scrub) \ + LOG_TAG(stacktrace) \ LOG_TAG(start) \ LOG_TAG(startuptime) \ LOG_TAG(state) \
--- a/hotspot/src/share/vm/prims/jvm.cpp Thu Mar 10 14:15:15 2016 +0100 +++ b/hotspot/src/share/vm/prims/jvm.cpp Thu Mar 10 13:43:47 2016 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, 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 @@ -514,19 +514,13 @@ JVM_END -JVM_ENTRY(jint, JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable)) - JVMWrapper("JVM_GetStackTraceDepth"); - oop exception = JNIHandles::resolve(throwable); - return java_lang_Throwable::get_stack_trace_depth(exception, THREAD); -JVM_END - - -JVM_ENTRY(jobject, JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint index)) - JVMWrapper("JVM_GetStackTraceElement"); - JvmtiVMObjectAllocEventCollector oam; // This ctor (throughout this module) may trigger a safepoint/GC - oop exception = JNIHandles::resolve(throwable); - oop element = java_lang_Throwable::get_stack_trace_element(exception, index, CHECK_NULL); - return JNIHandles::make_local(env, element); +JVM_ENTRY(void, JVM_GetStackTraceElements(JNIEnv *env, jobject throwable, jobjectArray stackTrace)) + JVMWrapper("JVM_GetStackTraceElements"); + Handle exception(THREAD, JNIHandles::resolve(throwable)); + objArrayOop st = objArrayOop(JNIHandles::resolve(stackTrace)); + objArrayHandle stack_trace(THREAD, st); + // Fill in the allocated stack trace + java_lang_Throwable::get_stack_trace_elements(exception, stack_trace, CHECK); JVM_END
--- a/hotspot/src/share/vm/prims/jvm.h Thu Mar 10 14:15:15 2016 +0100 +++ b/hotspot/src/share/vm/prims/jvm.h Thu Mar 10 13:43:47 2016 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, 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 @@ -201,11 +201,8 @@ JNIEXPORT void JNICALL JVM_FillInStackTrace(JNIEnv *env, jobject throwable); -JNIEXPORT jint JNICALL -JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable); - -JNIEXPORT jobject JNICALL -JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint index); +JNIEXPORT void JNICALL +JVM_GetStackTraceElements(JNIEnv *env, jobject throwable, jobjectArray elements); /* * java.lang.StackWalker
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/Throwable/StackTraceLogging.java Thu Mar 10 13:43:47 2016 -0500 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8150778 + * @summary check stacktrace logging + * @library /testlibrary + * @modules java.base/sun.misc + * java.management + * @build jdk.test.lib.OutputAnalyzer jdk.test.lib.ProcessTools + * @compile TestThrowable.java + * @run driver StackTraceLogging + */ + +import java.io.File; +import java.util.Map; +import jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.ProcessTools; + +public class StackTraceLogging { + static void updateEnvironment(ProcessBuilder pb, String environmentVariable, String value) { + Map<String, String> env = pb.environment(); + env.put(environmentVariable, value); + } + + static void analyzeOutputOn(ProcessBuilder pb) throws Exception { + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + // These depths match the ones in TestThrowable.java + int[] depths = {10, 34, 100, 1024}; + for (int d : depths) { + output.shouldContain("java.lang.RuntimeException, " + d); + } + output.shouldHaveExitValue(0); + } + + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xlog:stacktrace=info", + "-XX:MaxJavaStackTraceDepth=1024", + "TestThrowable"); + analyzeOutputOn(pb); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/Throwable/TestThrowable.java Thu Mar 10 13:43:47 2016 -0500 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8150778 + * @summary Test exception depths, and code to get stack traces + * @library /testlibrary + * @run main/othervm -XX:MaxJavaStackTraceDepth=1024 TestThrowable + */ + +import java.lang.reflect.Field; +import jdk.test.lib.Asserts; + +public class TestThrowable { + + // Inner class that throws a lot of exceptions + static class Thrower { + static int MaxJavaStackTraceDepth = 1024; // as above + int[] depths = {10, 34, 100, 1024, 2042}; + int count = 0; + + int getDepth(Throwable t) throws Exception { + Field f = Throwable.class.getDeclaredField("depth"); + f.setAccessible(true); // it's private + return f.getInt(t); + } + + void callThrow(int depth) { + if (++count < depth) { + callThrow(depth); + } else { + throw new RuntimeException("depth tested " + depth); + } + } + void testThrow() throws Exception { + for (int d : depths) { + try { + count = getDepth(new Throwable()); + callThrow(d); + } catch(Exception e) { + e.getStackTrace(); + System.out.println(e.getMessage()); + int throwableDepth = getDepth(e); + Asserts.assertTrue(throwableDepth == d || + (d > MaxJavaStackTraceDepth && throwableDepth == MaxJavaStackTraceDepth), + "depth should return the correct value: depth tested=" + + d + " throwableDepth=" + throwableDepth); + } + } + } + } + + public static void main(String... unused) throws Exception { + Thrower t = new Thrower(); + t.testThrow(); + } +}