8058345: Refactor native stack printing from vmError.cpp to debug.cpp to make it available in gdb as well

Also fix stack trace on x86 to enable walking of runtime stubs and native wrappers

Reviewed-by: kvn
This commit is contained in:
Volker Simonis 2014-09-24 12:19:07 -07:00
parent f85f7d2832
commit a1628426d3
12 changed files with 117 additions and 41 deletions

View File

@ -308,3 +308,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

View File

@ -343,7 +343,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
@ -365,6 +365,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),
@ -419,6 +423,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());
}

View File

@ -163,6 +163,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.

View File

@ -715,3 +715,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

View File

@ -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; }

View File

@ -41,7 +41,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;
@ -59,6 +59,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;

View File

@ -438,3 +438,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

View File

@ -265,7 +265,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);
}

View File

@ -68,6 +68,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.

View File

@ -653,6 +653,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");
@ -665,3 +672,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("...<more frames>...");
}
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

View File

@ -263,4 +263,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

View File

@ -577,7 +577,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.
@ -585,43 +585,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("...<more frames>...");
}
st->cr();
}
print_native_stack(st, fr, _thread, buf, sizeof(buf));
}
}