From e07b4d15a6317b628721b37f10996c64e59935de Mon Sep 17 00:00:00 2001 From: simonis Date: Wed, 24 Sep 2014 12:19:07 -0700 Subject: [PATCH] 8058345: Refactor native stack printing from vmError.cpp to debug.cpp to make it available in gdb as well Summary: Also fix stack trace on x86 to enable walking of runtime stubs and native wrappers Reviewed-by: kvn --- src/cpu/ppc/vm/frame_ppc.cpp | 7 +++ src/cpu/sparc/vm/frame_sparc.cpp | 13 +++- src/cpu/sparc/vm/frame_sparc.hpp | 2 + src/cpu/x86/vm/frame_x86.cpp | 7 +++ src/cpu/x86/vm/frame_x86.hpp | 2 + src/cpu/x86/vm/frame_x86.inline.hpp | 6 +- src/cpu/zero/vm/frame_zero.cpp | 7 +++ .../solaris_sparc/vm/os_solaris_sparc.cpp | 2 +- src/share/vm/runtime/frame.hpp | 9 +++ src/share/vm/utilities/debug.cpp | 60 +++++++++++++++++++ src/share/vm/utilities/debug.hpp | 3 + src/share/vm/utilities/vmError.cpp | 40 +------------ 12 files changed, 117 insertions(+), 41 deletions(-) diff --git a/src/cpu/ppc/vm/frame_ppc.cpp b/src/cpu/ppc/vm/frame_ppc.cpp index ff177e623..a4f7bedb0 100644 --- a/src/cpu/ppc/vm/frame_ppc.cpp +++ b/src/cpu/ppc/vm/frame_ppc.cpp @@ -318,3 +318,10 @@ intptr_t *frame::initial_deoptimization_info() { // unused... but returns fp() to minimize changes introduced by 7087445 return fp(); } + +#ifndef PRODUCT +// This is a generic constructor which is only used by pns() in debug.cpp. +frame::frame(void* sp, void* fp, void* pc) : _sp((intptr_t*)sp), _unextended_sp((intptr_t*)sp) { + find_codeblob_and_set_pc_and_deopt_state((address)pc); // also sets _fp and adjusts _unextended_sp +} +#endif diff --git a/src/cpu/sparc/vm/frame_sparc.cpp b/src/cpu/sparc/vm/frame_sparc.cpp index 6ce05bdfc..a1f1bca0a 100644 --- a/src/cpu/sparc/vm/frame_sparc.cpp +++ b/src/cpu/sparc/vm/frame_sparc.cpp @@ -342,7 +342,7 @@ bool frame::safe_for_sender(JavaThread *thread) { // constructors // Construct an unpatchable, deficient frame -frame::frame(intptr_t* sp, unpatchable_t, address pc, CodeBlob* cb) { +void frame::init(intptr_t* sp, address pc, CodeBlob* cb) { #ifdef _LP64 assert( (((intptr_t)sp & (wordSize-1)) == 0), "frame constructor passed an invalid sp"); #endif @@ -364,6 +364,10 @@ frame::frame(intptr_t* sp, unpatchable_t, address pc, CodeBlob* cb) { #endif // ASSERT } +frame::frame(intptr_t* sp, unpatchable_t, address pc, CodeBlob* cb) { + init(sp, pc, cb); +} + frame::frame(intptr_t* sp, intptr_t* younger_sp, bool younger_frame_is_interpreted) : _sp(sp), _younger_sp(younger_sp), @@ -418,6 +422,13 @@ frame::frame(intptr_t* sp, intptr_t* younger_sp, bool younger_frame_is_interpret } } +#ifndef PRODUCT +// This is a generic constructor which is only used by pns() in debug.cpp. +frame::frame(void* sp, void* fp, void* pc) { + init((intptr_t*)sp, (address)pc, NULL); +} +#endif + bool frame::is_interpreted_frame() const { return Interpreter::contains(pc()); } diff --git a/src/cpu/sparc/vm/frame_sparc.hpp b/src/cpu/sparc/vm/frame_sparc.hpp index 51a014872..916444ae6 100644 --- a/src/cpu/sparc/vm/frame_sparc.hpp +++ b/src/cpu/sparc/vm/frame_sparc.hpp @@ -164,6 +164,8 @@ enum unpatchable_t { unpatchable }; frame(intptr_t* sp, unpatchable_t, address pc = NULL, CodeBlob* cb = NULL); + void init(intptr_t* sp, address pc, CodeBlob* cb); + // Walk from sp outward looking for old_sp, and return old_sp's predecessor // (i.e. return the sp from the frame where old_sp is the fp). // Register windows are assumed to be flushed for the stack in question. diff --git a/src/cpu/x86/vm/frame_x86.cpp b/src/cpu/x86/vm/frame_x86.cpp index 212cdac3d..013562cd7 100644 --- a/src/cpu/x86/vm/frame_x86.cpp +++ b/src/cpu/x86/vm/frame_x86.cpp @@ -717,3 +717,10 @@ intptr_t* frame::real_fp() const { assert(! is_compiled_frame(), "unknown compiled frame size"); return fp(); } + +#ifndef PRODUCT +// This is a generic constructor which is only used by pns() in debug.cpp. +frame::frame(void* sp, void* fp, void* pc) { + init((intptr_t*)sp, (intptr_t*)fp, (address)pc); +} +#endif diff --git a/src/cpu/x86/vm/frame_x86.hpp b/src/cpu/x86/vm/frame_x86.hpp index 8c6a72c2e..2daa5d674 100644 --- a/src/cpu/x86/vm/frame_x86.hpp +++ b/src/cpu/x86/vm/frame_x86.hpp @@ -187,6 +187,8 @@ frame(intptr_t* sp, intptr_t* fp); + void init(intptr_t* sp, intptr_t* fp, address pc); + // accessors for the instance variables // Note: not necessarily the real 'frame pointer' (see real_fp) intptr_t* fp() const { return _fp; } diff --git a/src/cpu/x86/vm/frame_x86.inline.hpp b/src/cpu/x86/vm/frame_x86.inline.hpp index 9b875b834..6dda6f34e 100644 --- a/src/cpu/x86/vm/frame_x86.inline.hpp +++ b/src/cpu/x86/vm/frame_x86.inline.hpp @@ -40,7 +40,7 @@ inline frame::frame() { _deopt_state = unknown; } -inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) { +inline void frame::init(intptr_t* sp, intptr_t* fp, address pc) { _sp = sp; _unextended_sp = sp; _fp = fp; @@ -58,6 +58,10 @@ inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) { } } +inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) { + init(sp, fp, pc); +} + inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc) { _sp = sp; _unextended_sp = unextended_sp; diff --git a/src/cpu/zero/vm/frame_zero.cpp b/src/cpu/zero/vm/frame_zero.cpp index 4be75fdc3..da57209de 100644 --- a/src/cpu/zero/vm/frame_zero.cpp +++ b/src/cpu/zero/vm/frame_zero.cpp @@ -441,3 +441,10 @@ intptr_t *frame::initial_deoptimization_info() { // unused... but returns fp() to minimize changes introduced by 7087445 return fp(); } + +#ifndef PRODUCT +// This is a generic constructor which is only used by pns() in debug.cpp. +frame::frame(void* sp, void* fp, void* pc) { + Unimplemented(); +} +#endif diff --git a/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp b/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp index d8c08fd4a..1785dc374 100644 --- a/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp +++ b/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp @@ -264,7 +264,7 @@ frame os::current_frame() { CAST_FROM_FN_PTR(address, os::current_frame)); if (os::is_first_C_frame(&myframe)) { // stack is not walkable - return frame(NULL, NULL, NULL); + return frame(NULL, NULL, false); } else { return os::get_sender_for_C_frame(&myframe); } diff --git a/src/share/vm/runtime/frame.hpp b/src/share/vm/runtime/frame.hpp index f715f68b5..2e5685b7d 100644 --- a/src/share/vm/runtime/frame.hpp +++ b/src/share/vm/runtime/frame.hpp @@ -91,6 +91,15 @@ class frame VALUE_OBJ_CLASS_SPEC { // Constructors frame(); +#ifndef PRODUCT + // This is a generic constructor which is only used by pns() in debug.cpp. + // pns (i.e. print native stack) uses this constructor to create a starting + // frame for stack walking. The implementation of this constructor is platform + // dependent (i.e. SPARC doesn't need an 'fp' argument an will ignore it) but + // we want to keep the signature generic because pns() is shared code. + frame(void* sp, void* fp, void* pc); +#endif + // Accessors // pc: Returns the pc at which this frame will continue normally. diff --git a/src/share/vm/utilities/debug.cpp b/src/share/vm/utilities/debug.cpp index f39aa667c..0b5029d66 100644 --- a/src/share/vm/utilities/debug.cpp +++ b/src/share/vm/utilities/debug.cpp @@ -666,6 +666,13 @@ void help() { tty->print_cr(" pm(int pc) - print Method* given compiled PC"); tty->print_cr(" findm(intptr_t pc) - finds Method*"); tty->print_cr(" find(intptr_t x) - finds & prints nmethod/stub/bytecode/oop based on pointer into it"); + tty->print_cr(" pns(void* sp, void* fp, void* pc) - print native (i.e. mixed) stack trace. E.g."); + tty->print_cr(" pns($sp, $rbp, $pc) on Linux/amd64 and Solaris/amd64 or"); + tty->print_cr(" pns($sp, $ebp, $pc) on Linux/x86 or"); + tty->print_cr(" pns($sp, 0, $pc) on Linux/ppc64 or"); + tty->print_cr(" pns($sp + 0x7ff, 0, $pc) on Solaris/SPARC"); + tty->print_cr(" - in gdb do 'set overload-resolution off' before calling pns()"); + tty->print_cr(" - in dbx do 'frame 1' before calling pns()"); tty->print_cr("misc."); tty->print_cr(" flush() - flushes the log file"); @@ -678,3 +685,56 @@ void help() { } #endif // !PRODUCT + +void print_native_stack(outputStream* st, frame fr, Thread* t, char* buf, int buf_size) { + + // see if it's a valid frame + if (fr.pc()) { + st->print_cr("Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)"); + + int count = 0; + while (count++ < StackPrintLimit) { + fr.print_on_error(st, buf, buf_size); + st->cr(); + // Compiled code may use EBP register on x86 so it looks like + // non-walkable C frame. Use frame.sender() for java frames. + if (t && t->is_Java_thread()) { + // Catch very first native frame by using stack address. + // For JavaThread stack_base and stack_size should be set. + if (!t->on_local_stack((address)(fr.real_fp() + 1))) { + break; + } + if (fr.is_java_frame() || fr.is_native_frame() || fr.is_runtime_frame()) { + RegisterMap map((JavaThread*)t, false); // No update + fr = fr.sender(&map); + } else { + fr = os::get_sender_for_C_frame(&fr); + } + } else { + // is_first_C_frame() does only simple checks for frame pointer, + // it will pass if java compiled code has a pointer in EBP. + if (os::is_first_C_frame(&fr)) break; + fr = os::get_sender_for_C_frame(&fr); + } + } + + if (count > StackPrintLimit) { + st->print_cr("......"); + } + + st->cr(); + } +} + +#ifndef PRODUCT + +extern "C" void pns(void* sp, void* fp, void* pc) { // print native stack + Command c("pns"); + static char buf[O_BUFLEN]; + Thread* t = ThreadLocalStorage::get_thread_slow(); + // Call generic frame constructor (certain arguments may be ignored) + frame fr(sp, fp, pc); + print_native_stack(tty, fr, t, buf, sizeof(buf)); +} + +#endif // !PRODUCT diff --git a/src/share/vm/utilities/debug.hpp b/src/share/vm/utilities/debug.hpp index 5c8f2c3e7..5b47e6142 100644 --- a/src/share/vm/utilities/debug.hpp +++ b/src/share/vm/utilities/debug.hpp @@ -265,4 +265,7 @@ NOT_PRODUCT(void test_error_handler();) void pd_ps(frame f); void pd_obfuscate_location(char *buf, size_t buflen); +class outputStream; +void print_native_stack(outputStream* st, frame fr, Thread* t, char* buf, int buf_size); + #endif // SHARE_VM_UTILITIES_DEBUG_HPP diff --git a/src/share/vm/utilities/vmError.cpp b/src/share/vm/utilities/vmError.cpp index 0375207b0..20346f4c2 100644 --- a/src/share/vm/utilities/vmError.cpp +++ b/src/share/vm/utilities/vmError.cpp @@ -575,7 +575,7 @@ void VMError::report(outputStream* st) { STEP(120, "(printing native stack)" ) - if (_verbose) { + if (_verbose) { if (os::platform_print_native_stack(st, _context, buf, sizeof(buf))) { // We have printed the native stack in platform-specific code // Windows/x64 needs special handling. @@ -583,43 +583,7 @@ void VMError::report(outputStream* st) { frame fr = _context ? os::fetch_frame_from_context(_context) : os::current_frame(); - // see if it's a valid frame - if (fr.pc()) { - st->print_cr("Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)"); - - - int count = 0; - while (count++ < StackPrintLimit) { - fr.print_on_error(st, buf, sizeof(buf)); - st->cr(); - // Compiled code may use EBP register on x86 so it looks like - // non-walkable C frame. Use frame.sender() for java frames. - if (_thread && _thread->is_Java_thread()) { - // Catch very first native frame by using stack address. - // For JavaThread stack_base and stack_size should be set. - if (!_thread->on_local_stack((address)(fr.sender_sp() + 1))) { - break; - } - if (fr.is_java_frame()) { - RegisterMap map((JavaThread*)_thread, false); // No update - fr = fr.sender(&map); - } else { - fr = os::get_sender_for_C_frame(&fr); - } - } else { - // is_first_C_frame() does only simple checks for frame pointer, - // it will pass if java compiled code has a pointer in EBP. - if (os::is_first_C_frame(&fr)) break; - fr = os::get_sender_for_C_frame(&fr); - } - } - - if (count > StackPrintLimit) { - st->print_cr("......"); - } - - st->cr(); - } + print_native_stack(st, fr, _thread, buf, sizeof(buf)); } } -- GitLab