mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8254158: Consolidate per-platform stack overflow handling code
Reviewed-by: fparain, hseigel
This commit is contained in:
parent
715e24afb2
commit
ba5dc67a74
10 changed files with 168 additions and 513 deletions
|
@ -139,8 +139,6 @@ class Linux {
|
||||||
static intptr_t* ucontext_get_sp(const ucontext_t* uc);
|
static intptr_t* ucontext_get_sp(const ucontext_t* uc);
|
||||||
static intptr_t* ucontext_get_fp(const ucontext_t* uc);
|
static intptr_t* ucontext_get_fp(const ucontext_t* uc);
|
||||||
|
|
||||||
static bool get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr);
|
|
||||||
|
|
||||||
// GNU libc and libpthread version strings
|
// GNU libc and libpthread version strings
|
||||||
static const char *libc_version() { return _libc_version; }
|
static const char *libc_version() { return _libc_version; }
|
||||||
static const char *libpthread_version() { return _libpthread_version; }
|
static const char *libpthread_version() { return _libpthread_version; }
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "utilities/globalDefinitions.hpp"
|
#include "utilities/globalDefinitions.hpp"
|
||||||
#include "runtime/frame.inline.hpp"
|
#include "runtime/frame.inline.hpp"
|
||||||
#include "runtime/interfaceSupport.inline.hpp"
|
#include "runtime/interfaceSupport.inline.hpp"
|
||||||
|
#include "runtime/sharedRuntime.hpp"
|
||||||
#include "services/memTracker.hpp"
|
#include "services/memTracker.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
#include "runtime/java.hpp"
|
#include "runtime/java.hpp"
|
||||||
|
@ -907,6 +908,119 @@ size_t os::Posix::get_initial_stack_size(ThreadType thr_type, size_t req_stack_s
|
||||||
return stack_size;
|
return stack_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef ZERO
|
||||||
|
#ifndef ARM
|
||||||
|
static bool get_frame_at_stack_banging_point(JavaThread* thread, address pc, const void* ucVoid, frame* fr) {
|
||||||
|
if (Interpreter::contains(pc)) {
|
||||||
|
// interpreter performs stack banging after the fixed frame header has
|
||||||
|
// been generated while the compilers perform it before. To maintain
|
||||||
|
// semantic consistency between interpreted and compiled frames, the
|
||||||
|
// method returns the Java sender of the current frame.
|
||||||
|
*fr = os::fetch_frame_from_context(ucVoid);
|
||||||
|
if (!fr->is_first_java_frame()) {
|
||||||
|
// get_frame_at_stack_banging_point() is only called when we
|
||||||
|
// have well defined stacks so java_sender() calls do not need
|
||||||
|
// to assert safe_for_sender() first.
|
||||||
|
*fr = fr->java_sender();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// more complex code with compiled code
|
||||||
|
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
|
||||||
|
CodeBlob* cb = CodeCache::find_blob(pc);
|
||||||
|
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
|
||||||
|
// Not sure where the pc points to, fallback to default
|
||||||
|
// stack overflow handling
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// in compiled code, the stack banging is performed just after the return pc
|
||||||
|
// has been pushed on the stack
|
||||||
|
*fr = os::fetch_compiled_frame_from_context(ucVoid);
|
||||||
|
if (!fr->is_java_frame()) {
|
||||||
|
assert(!fr->is_first_frame(), "Safety check");
|
||||||
|
// See java_sender() comment above.
|
||||||
|
*fr = fr->java_sender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(fr->is_java_frame(), "Safety check");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // ARM
|
||||||
|
|
||||||
|
// This return true if the signal handler should just continue, ie. return after calling this
|
||||||
|
bool os::Posix::handle_stack_overflow(JavaThread* thread, address addr, address pc,
|
||||||
|
const void* ucVoid, address* stub) {
|
||||||
|
// stack overflow
|
||||||
|
StackOverflow* overflow_state = thread->stack_overflow_state();
|
||||||
|
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
|
||||||
|
if (thread->thread_state() == _thread_in_Java) {
|
||||||
|
#ifndef ARM
|
||||||
|
// arm32 doesn't have this
|
||||||
|
if (overflow_state->in_stack_reserved_zone(addr)) {
|
||||||
|
frame fr;
|
||||||
|
if (get_frame_at_stack_banging_point(thread, pc, ucVoid, &fr)) {
|
||||||
|
assert(fr.is_java_frame(), "Must be a Java frame");
|
||||||
|
frame activation =
|
||||||
|
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
|
||||||
|
if (activation.sp() != NULL) {
|
||||||
|
overflow_state->disable_stack_reserved_zone();
|
||||||
|
if (activation.is_interpreted_frame()) {
|
||||||
|
overflow_state->set_reserved_stack_activation((address)(
|
||||||
|
activation.fp() + frame::interpreter_frame_initial_sp_offset));
|
||||||
|
} else {
|
||||||
|
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
|
||||||
|
}
|
||||||
|
return true; // just continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ARM
|
||||||
|
// Throw a stack overflow exception. Guard pages will be reenabled
|
||||||
|
// while unwinding the stack.
|
||||||
|
overflow_state->disable_stack_yellow_reserved_zone();
|
||||||
|
*stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
|
||||||
|
} else {
|
||||||
|
// Thread was in the vm or native code. Return and try to finish.
|
||||||
|
overflow_state->disable_stack_yellow_reserved_zone();
|
||||||
|
return true; // just continue
|
||||||
|
}
|
||||||
|
} else if (overflow_state->in_stack_red_zone(addr)) {
|
||||||
|
// Fatal red zone violation. Disable the guard pages and fall through
|
||||||
|
// to handle_unexpected_exception way down below.
|
||||||
|
overflow_state->disable_stack_red_zone();
|
||||||
|
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
|
||||||
|
|
||||||
|
// This is a likely cause, but hard to verify. Let's just print
|
||||||
|
// it as a hint.
|
||||||
|
tty->print_raw_cr("Please check if any of your loaded .so files has "
|
||||||
|
"enabled executable stack (see man page execstack(8))");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
#if !defined(AIX) && !defined(__APPLE__)
|
||||||
|
// bsd and aix don't have this
|
||||||
|
|
||||||
|
// Accessing stack address below sp may cause SEGV if current
|
||||||
|
// thread has MAP_GROWSDOWN stack. This should only happen when
|
||||||
|
// current thread was created by user code with MAP_GROWSDOWN flag
|
||||||
|
// and then attached to VM. See notes in os_linux.cpp.
|
||||||
|
if (thread->osthread()->expanding_stack() == 0) {
|
||||||
|
thread->osthread()->set_expanding_stack();
|
||||||
|
if (os::Linux::manually_expand_stack(thread, addr)) {
|
||||||
|
thread->osthread()->clear_expanding_stack();
|
||||||
|
return true; // just continue
|
||||||
|
}
|
||||||
|
thread->osthread()->clear_expanding_stack();
|
||||||
|
} else {
|
||||||
|
fatal("recursive segv. expanding stack.");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
tty->print_raw_cr("SIGSEGV happened inside stack but outside yellow and red zone.");
|
||||||
|
#endif // AIX or BSD
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // ZERO
|
||||||
|
|
||||||
bool os::Posix::is_root(uid_t uid){
|
bool os::Posix::is_root(uid_t uid){
|
||||||
return ROOT_UID == uid;
|
return ROOT_UID == uid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,10 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void to_RTC_abstime(timespec* abstime, int64_t millis);
|
static void to_RTC_abstime(timespec* abstime, int64_t millis);
|
||||||
|
|
||||||
|
static bool handle_stack_overflow(JavaThread* thread, address addr, address pc,
|
||||||
|
const void* ucVoid,
|
||||||
|
address* stub);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -142,40 +142,11 @@ frame os::fetch_frame_from_context(const void* ucVoid) {
|
||||||
return fr;
|
return fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os::Aix::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) {
|
frame os::fetch_compiled_frame_from_context(const void* ucVoid) {
|
||||||
address pc = (address) os::Aix::ucontext_get_pc(uc);
|
const ucontext_t* uc = (const ucontext_t*)ucVoid;
|
||||||
if (Interpreter::contains(pc)) {
|
|
||||||
// Interpreter performs stack banging after the fixed frame header has
|
|
||||||
// been generated while the compilers perform it before. To maintain
|
|
||||||
// semantic consistency between interpreted and compiled frames, the
|
|
||||||
// method returns the Java sender of the current frame.
|
|
||||||
*fr = os::fetch_frame_from_context(uc);
|
|
||||||
if (!fr->is_first_java_frame()) {
|
|
||||||
assert(fr->safe_for_sender(thread), "Safety check");
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// More complex code with compiled code.
|
|
||||||
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
|
|
||||||
CodeBlob* cb = CodeCache::find_blob(pc);
|
|
||||||
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
|
|
||||||
// Not sure where the pc points to, fallback to default
|
|
||||||
// stack overflow handling. In compiled code, we bang before
|
|
||||||
// the frame is complete.
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
intptr_t* sp = os::Aix::ucontext_get_sp(uc);
|
intptr_t* sp = os::Aix::ucontext_get_sp(uc);
|
||||||
address lr = ucontext_get_lr(uc);
|
address lr = ucontext_get_lr(uc);
|
||||||
*fr = frame(sp, lr);
|
*fr = frame(sp, lr);
|
||||||
if (!fr->is_java_frame()) {
|
|
||||||
assert(fr->safe_for_sender(thread), "Safety check");
|
|
||||||
assert(!fr->is_first_frame(), "Safety check");
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(fr->is_java_frame(), "Safety check");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frame os::get_sender_for_C_frame(frame* fr) {
|
frame os::get_sender_for_C_frame(frame* fr) {
|
||||||
|
@ -267,56 +238,13 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec
|
||||||
// Handle ALL stack overflow variations here
|
// Handle ALL stack overflow variations here
|
||||||
if (sig == SIGSEGV && thread->is_in_full_stack(addr)) {
|
if (sig == SIGSEGV && thread->is_in_full_stack(addr)) {
|
||||||
// stack overflow
|
// stack overflow
|
||||||
StackOverflow* overflow_state = thread->stack_overflow_state();
|
if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) {
|
||||||
|
return 1; // continue
|
||||||
//
|
} else if (stub != NULL) {
|
||||||
// If we are in a yellow zone and we are inside java, we disable the yellow zone and
|
|
||||||
// throw a stack overflow exception.
|
|
||||||
// If we are in native code or VM C code, we report-and-die. The original coding tried
|
|
||||||
// to continue with yellow zone disabled, but that doesn't buy us much and prevents
|
|
||||||
// hs_err_pid files.
|
|
||||||
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
|
|
||||||
if (thread->thread_state() == _thread_in_Java) {
|
|
||||||
if (overflow_state->in_stack_reserved_zone(addr)) {
|
|
||||||
frame fr;
|
|
||||||
if (os::Aix::get_frame_at_stack_banging_point(thread, uc, &fr)) {
|
|
||||||
assert(fr.is_java_frame(), "Must be a Javac frame");
|
|
||||||
frame activation =
|
|
||||||
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
|
|
||||||
if (activation.sp() != NULL) {
|
|
||||||
overflow_state->disable_stack_reserved_zone();
|
|
||||||
if (activation.is_interpreted_frame()) {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)activation.fp());
|
|
||||||
} else {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Throw a stack overflow exception.
|
|
||||||
// Guard pages will be reenabled while unwinding the stack.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
|
|
||||||
goto run_stub;
|
goto run_stub;
|
||||||
} else {
|
} else {
|
||||||
// Thread was in the vm or native code. Return and try to finish.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if (overflow_state->in_stack_red_zone(addr)) {
|
|
||||||
// Fatal red zone violation. Disable the guard pages and fall through
|
|
||||||
// to handle_unexpected_exception way down below.
|
|
||||||
overflow_state->disable_stack_red_zone();
|
|
||||||
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
|
|
||||||
goto report_and_die;
|
|
||||||
} else {
|
|
||||||
// This means a segv happened inside our stack, but not in
|
|
||||||
// the guarded zone. I'd like to know when this happens,
|
|
||||||
tty->print_raw_cr("SIGSEGV happened inside stack but outside yellow and red zone.");
|
|
||||||
goto report_and_die;
|
goto report_and_die;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end handle SIGSEGV inside stack boundaries
|
} // end handle SIGSEGV inside stack boundaries
|
||||||
|
|
||||||
if (thread->thread_state() == _thread_in_Java) {
|
if (thread->thread_state() == _thread_in_Java) {
|
||||||
|
|
|
@ -339,41 +339,12 @@ frame os::fetch_frame_from_context(const void* ucVoid) {
|
||||||
return frame(sp, fp, epc);
|
return frame(sp, fp, epc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os::Bsd::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) {
|
frame os::fetch_compiled_frame_from_context(const void* ucVoid) {
|
||||||
address pc = (address) os::Bsd::ucontext_get_pc(uc);
|
const ucontext_t* uc = (const ucontext_t*)ucVoid;
|
||||||
if (Interpreter::contains(pc)) {
|
frame fr = os::fetch_frame_from_context(uc);
|
||||||
// interpreter performs stack banging after the fixed frame header has
|
|
||||||
// been generated while the compilers perform it before. To maintain
|
|
||||||
// semantic consistency between interpreted and compiled frames, the
|
|
||||||
// method returns the Java sender of the current frame.
|
|
||||||
*fr = os::fetch_frame_from_context(uc);
|
|
||||||
if (!fr->is_first_java_frame()) {
|
|
||||||
// get_frame_at_stack_banging_point() is only called when we
|
|
||||||
// have well defined stacks so java_sender() calls do not need
|
|
||||||
// to assert safe_for_sender() first.
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// more complex code with compiled code
|
|
||||||
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
|
|
||||||
CodeBlob* cb = CodeCache::find_blob(pc);
|
|
||||||
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
|
|
||||||
// Not sure where the pc points to, fallback to default
|
|
||||||
// stack overflow handling
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
*fr = os::fetch_frame_from_context(uc);
|
|
||||||
// in compiled code, the stack banging is performed just after the return pc
|
// in compiled code, the stack banging is performed just after the return pc
|
||||||
// has been pushed on the stack
|
// has been pushed on the stack
|
||||||
*fr = frame(fr->sp() + 1, fr->fp(), (address)*(fr->sp()));
|
return frame(fr.sp() + 1, fr.fp(), (address)*(fr.sp()));
|
||||||
if (!fr->is_java_frame()) {
|
|
||||||
// See java_sender() comment above.
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(fr->is_java_frame(), "Safety check");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default, gcc always save frame pointer (%ebp/%rbp) on stack. It may get
|
// By default, gcc always save frame pointer (%ebp/%rbp) on stack. It may get
|
||||||
|
@ -495,40 +466,8 @@ JVM_handle_bsd_signal(int sig,
|
||||||
// check if fault address is within thread stack
|
// check if fault address is within thread stack
|
||||||
if (thread->is_in_full_stack(addr)) {
|
if (thread->is_in_full_stack(addr)) {
|
||||||
// stack overflow
|
// stack overflow
|
||||||
StackOverflow* overflow_state = thread->stack_overflow_state();
|
if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) {
|
||||||
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
|
return 1; // continue
|
||||||
if (thread->thread_state() == _thread_in_Java) {
|
|
||||||
if (overflow_state->in_stack_reserved_zone(addr)) {
|
|
||||||
frame fr;
|
|
||||||
if (os::Bsd::get_frame_at_stack_banging_point(thread, uc, &fr)) {
|
|
||||||
assert(fr.is_java_frame(), "Must be a Java frame");
|
|
||||||
frame activation = SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
|
|
||||||
if (activation.sp() != NULL) {
|
|
||||||
overflow_state->disable_stack_reserved_zone();
|
|
||||||
if (activation.is_interpreted_frame()) {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)(
|
|
||||||
activation.fp() + frame::interpreter_frame_initial_sp_offset));
|
|
||||||
} else {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Throw a stack overflow exception. Guard pages will be reenabled
|
|
||||||
// while unwinding the stack.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
|
|
||||||
} else {
|
|
||||||
// Thread was in the vm or native code. Return and try to finish.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if (overflow_state->in_stack_red_zone(addr)) {
|
|
||||||
// Fatal red zone violation. Disable the guard pages and fall through
|
|
||||||
// to handle_unexpected_exception way down below.
|
|
||||||
overflow_state->disable_stack_red_zone();
|
|
||||||
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,27 +133,8 @@ frame os::fetch_frame_from_context(const void* ucVoid) {
|
||||||
return frame(sp, fp, epc);
|
return frame(sp, fp, epc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os::Linux::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) {
|
frame os::fetch_compiled_frame_from_context(const void* ucVoid) {
|
||||||
address pc = (address) os::Linux::ucontext_get_pc(uc);
|
const ucontext_t* uc = (const ucontext_t*)ucVoid;
|
||||||
if (Interpreter::contains(pc)) {
|
|
||||||
// interpreter performs stack banging after the fixed frame header has
|
|
||||||
// been generated while the compilers perform it before. To maintain
|
|
||||||
// semantic consistency between interpreted and compiled frames, the
|
|
||||||
// method returns the Java sender of the current frame.
|
|
||||||
*fr = os::fetch_frame_from_context(uc);
|
|
||||||
if (!fr->is_first_java_frame()) {
|
|
||||||
assert(fr->safe_for_sender(thread), "Safety check");
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// more complex code with compiled code
|
|
||||||
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
|
|
||||||
CodeBlob* cb = CodeCache::find_blob(pc);
|
|
||||||
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
|
|
||||||
// Not sure where the pc points to, fallback to default
|
|
||||||
// stack overflow handling
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// In compiled code, the stack banging is performed before LR
|
// In compiled code, the stack banging is performed before LR
|
||||||
// has been saved in the frame. LR is live, and SP and FP
|
// has been saved in the frame. LR is live, and SP and FP
|
||||||
// belong to the caller.
|
// belong to the caller.
|
||||||
|
@ -161,16 +142,7 @@ bool os::Linux::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t*
|
||||||
intptr_t* sp = os::Linux::ucontext_get_sp(uc);
|
intptr_t* sp = os::Linux::ucontext_get_sp(uc);
|
||||||
address pc = (address)(uc->uc_mcontext.regs[REG_LR]
|
address pc = (address)(uc->uc_mcontext.regs[REG_LR]
|
||||||
- NativeInstruction::instruction_size);
|
- NativeInstruction::instruction_size);
|
||||||
*fr = frame(sp, fp, pc);
|
return frame(sp, fp, pc);
|
||||||
if (!fr->is_java_frame()) {
|
|
||||||
assert(fr->safe_for_sender(thread), "Safety check");
|
|
||||||
assert(!fr->is_first_frame(), "Safety check");
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(fr->is_java_frame(), "Safety check");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default, gcc always saves frame pointer rfp on this stack. This
|
// By default, gcc always saves frame pointer rfp on this stack. This
|
||||||
|
@ -278,62 +250,8 @@ JVM_handle_linux_signal(int sig,
|
||||||
if (sig == SIGSEGV) {
|
if (sig == SIGSEGV) {
|
||||||
// check if fault address is within thread stack
|
// check if fault address is within thread stack
|
||||||
if (thread->is_in_full_stack(addr)) {
|
if (thread->is_in_full_stack(addr)) {
|
||||||
StackOverflow* overflow_state = thread->stack_overflow_state();
|
if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) {
|
||||||
// stack overflow
|
return 1; // continue
|
||||||
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
|
|
||||||
if (thread->thread_state() == _thread_in_Java) {
|
|
||||||
if (overflow_state->in_stack_reserved_zone(addr)) {
|
|
||||||
frame fr;
|
|
||||||
if (os::Linux::get_frame_at_stack_banging_point(thread, uc, &fr)) {
|
|
||||||
assert(fr.is_java_frame(), "Must be a Java frame");
|
|
||||||
frame activation =
|
|
||||||
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
|
|
||||||
if (activation.sp() != NULL) {
|
|
||||||
overflow_state->disable_stack_reserved_zone();
|
|
||||||
if (activation.is_interpreted_frame()) {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)(
|
|
||||||
activation.fp() + frame::interpreter_frame_initial_sp_offset));
|
|
||||||
} else {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Throw a stack overflow exception. Guard pages will be reenabled
|
|
||||||
// while unwinding the stack.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
|
|
||||||
} else {
|
|
||||||
// Thread was in the vm or native code. Return and try to finish.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if (overflow_state->in_stack_red_zone(addr)) {
|
|
||||||
// Fatal red zone violation. Disable the guard pages and fall through
|
|
||||||
// to handle_unexpected_exception way down below.
|
|
||||||
overflow_state->disable_stack_red_zone();
|
|
||||||
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
|
|
||||||
|
|
||||||
// This is a likely cause, but hard to verify. Let's just print
|
|
||||||
// it as a hint.
|
|
||||||
tty->print_raw_cr("Please check if any of your loaded .so files has "
|
|
||||||
"enabled executable stack (see man page execstack(8))");
|
|
||||||
} else {
|
|
||||||
// Accessing stack address below sp may cause SEGV if current
|
|
||||||
// thread has MAP_GROWSDOWN stack. This should only happen when
|
|
||||||
// current thread was created by user code with MAP_GROWSDOWN flag
|
|
||||||
// and then attached to VM. See notes in os_linux.cpp.
|
|
||||||
if (thread->osthread()->expanding_stack() == 0) {
|
|
||||||
thread->osthread()->set_expanding_stack();
|
|
||||||
if (os::Linux::manually_expand_stack(thread, addr)) {
|
|
||||||
thread->osthread()->clear_expanding_stack();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
thread->osthread()->clear_expanding_stack();
|
|
||||||
} else {
|
|
||||||
fatal("recursive segv. expanding stack.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,40 +162,11 @@ frame os::fetch_frame_from_context(const void* ucVoid) {
|
||||||
return frame(sp, epc);
|
return frame(sp, epc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os::Linux::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) {
|
frame os::fetch_compiled_frame_from_context(const void* ucVoid) {
|
||||||
address pc = (address) os::Linux::ucontext_get_pc(uc);
|
const ucontext_t* uc = (const ucontext_t*)ucVoid;
|
||||||
if (Interpreter::contains(pc)) {
|
|
||||||
// Interpreter performs stack banging after the fixed frame header has
|
|
||||||
// been generated while the compilers perform it before. To maintain
|
|
||||||
// semantic consistency between interpreted and compiled frames, the
|
|
||||||
// method returns the Java sender of the current frame.
|
|
||||||
*fr = os::fetch_frame_from_context(uc);
|
|
||||||
if (!fr->is_first_java_frame()) {
|
|
||||||
assert(fr->safe_for_sender(thread), "Safety check");
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// More complex code with compiled code.
|
|
||||||
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
|
|
||||||
CodeBlob* cb = CodeCache::find_blob(pc);
|
|
||||||
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
|
|
||||||
// Not sure where the pc points to, fallback to default
|
|
||||||
// stack overflow handling. In compiled code, we bang before
|
|
||||||
// the frame is complete.
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
intptr_t* sp = os::Linux::ucontext_get_sp(uc);
|
intptr_t* sp = os::Linux::ucontext_get_sp(uc);
|
||||||
address lr = ucontext_get_lr(uc);
|
address lr = ucontext_get_lr(uc);
|
||||||
*fr = frame(sp, lr);
|
return frame(sp, lr);
|
||||||
if (!fr->is_java_frame()) {
|
|
||||||
assert(fr->safe_for_sender(thread), "Safety check");
|
|
||||||
assert(!fr->is_first_frame(), "Safety check");
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(fr->is_java_frame(), "Safety check");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frame os::get_sender_for_C_frame(frame* fr) {
|
frame os::get_sender_for_C_frame(frame* fr) {
|
||||||
|
@ -327,60 +298,8 @@ JVM_handle_linux_signal(int sig,
|
||||||
// Check if fault address is within thread stack.
|
// Check if fault address is within thread stack.
|
||||||
if (thread->is_in_full_stack(addr)) {
|
if (thread->is_in_full_stack(addr)) {
|
||||||
// stack overflow
|
// stack overflow
|
||||||
StackOverflow* overflow_state = thread->stack_overflow_state();
|
if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) {
|
||||||
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
|
return 1; // continue
|
||||||
if (thread->thread_state() == _thread_in_Java) {
|
|
||||||
if (overflow_state->in_stack_reserved_zone(addr)) {
|
|
||||||
frame fr;
|
|
||||||
if (os::Linux::get_frame_at_stack_banging_point(thread, uc, &fr)) {
|
|
||||||
assert(fr.is_java_frame(), "Must be a Javac frame");
|
|
||||||
frame activation =
|
|
||||||
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
|
|
||||||
if (activation.sp() != NULL) {
|
|
||||||
overflow_state->disable_stack_reserved_zone();
|
|
||||||
if (activation.is_interpreted_frame()) {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)activation.fp());
|
|
||||||
} else {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Throw a stack overflow exception.
|
|
||||||
// Guard pages will be reenabled while unwinding the stack.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
|
|
||||||
} else {
|
|
||||||
// Thread was in the vm or native code. Return and try to finish.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if (overflow_state->in_stack_red_zone(addr)) {
|
|
||||||
// Fatal red zone violation. Disable the guard pages and fall through
|
|
||||||
// to handle_unexpected_exception way down below.
|
|
||||||
overflow_state->disable_stack_red_zone();
|
|
||||||
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
|
|
||||||
|
|
||||||
// This is a likely cause, but hard to verify. Let's just print
|
|
||||||
// it as a hint.
|
|
||||||
tty->print_raw_cr("Please check if any of your loaded .so files has "
|
|
||||||
"enabled executable stack (see man page execstack(8))");
|
|
||||||
} else {
|
|
||||||
// Accessing stack address below sp may cause SEGV if current
|
|
||||||
// thread has MAP_GROWSDOWN stack. This should only happen when
|
|
||||||
// current thread was created by user code with MAP_GROWSDOWN flag
|
|
||||||
// and then attached to VM. See notes in os_linux.cpp.
|
|
||||||
if (thread->osthread()->expanding_stack() == 0) {
|
|
||||||
thread->osthread()->set_expanding_stack();
|
|
||||||
if (os::Linux::manually_expand_stack(thread, addr)) {
|
|
||||||
thread->osthread()->clear_expanding_stack();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
thread->osthread()->clear_expanding_stack();
|
|
||||||
} else {
|
|
||||||
fatal("recursive segv. expanding stack.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,40 +145,11 @@ frame os::fetch_frame_from_context(const void* ucVoid) {
|
||||||
return frame(sp, epc);
|
return frame(sp, epc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os::Linux::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) {
|
frame os::fetch_compiled_frame_from_context(const void* ucVoid) {
|
||||||
address pc = (address) os::Linux::ucontext_get_pc(uc);
|
const ucontext_t* uc = (const ucontext_t*)ucVoid;
|
||||||
if (Interpreter::contains(pc)) {
|
|
||||||
// Interpreter performs stack banging after the fixed frame header has
|
|
||||||
// been generated while the compilers perform it before. To maintain
|
|
||||||
// semantic consistency between interpreted and compiled frames, the
|
|
||||||
// method returns the Java sender of the current frame.
|
|
||||||
*fr = os::fetch_frame_from_context(uc);
|
|
||||||
if (!fr->is_first_java_frame()) {
|
|
||||||
assert(fr->safe_for_sender(thread), "Safety check");
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// More complex code with compiled code.
|
|
||||||
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
|
|
||||||
CodeBlob* cb = CodeCache::find_blob(pc);
|
|
||||||
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
|
|
||||||
// Not sure where the pc points to, fallback to default
|
|
||||||
// stack overflow handling. In compiled code, we bang before
|
|
||||||
// the frame is complete.
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
intptr_t* sp = os::Linux::ucontext_get_sp(uc);
|
intptr_t* sp = os::Linux::ucontext_get_sp(uc);
|
||||||
address lr = ucontext_get_lr(uc);
|
address lr = ucontext_get_lr(uc);
|
||||||
*fr = frame(sp, lr);
|
return frame(sp, lr);
|
||||||
if (!fr->is_java_frame()) {
|
|
||||||
assert(fr->safe_for_sender(thread), "Safety check");
|
|
||||||
assert(!fr->is_first_frame(), "Safety check");
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(fr->is_java_frame(), "Safety check");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frame os::get_sender_for_C_frame(frame* fr) {
|
frame os::get_sender_for_C_frame(frame* fr) {
|
||||||
|
@ -323,60 +294,8 @@ JVM_handle_linux_signal(int sig,
|
||||||
// Check if fault address is within thread stack.
|
// Check if fault address is within thread stack.
|
||||||
if (thread->is_in_full_stack(addr)) {
|
if (thread->is_in_full_stack(addr)) {
|
||||||
// stack overflow
|
// stack overflow
|
||||||
StackOverflow* overflow_state = thread->stack_overflow_state();
|
if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) {
|
||||||
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
|
return 1; // continue
|
||||||
if (thread->thread_state() == _thread_in_Java) {
|
|
||||||
if (overflow_state->in_stack_reserved_zone(addr)) {
|
|
||||||
frame fr;
|
|
||||||
if (os::Linux::get_frame_at_stack_banging_point(thread, uc, &fr)) {
|
|
||||||
assert(fr.is_java_frame(), "Must be a Javac frame");
|
|
||||||
frame activation =
|
|
||||||
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
|
|
||||||
if (activation.sp() != NULL) {
|
|
||||||
overflow_state->disable_stack_reserved_zone();
|
|
||||||
if (activation.is_interpreted_frame()) {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)activation.fp());
|
|
||||||
} else {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Throw a stack overflow exception.
|
|
||||||
// Guard pages will be reenabled while unwinding the stack.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
|
|
||||||
} else {
|
|
||||||
// Thread was in the vm or native code. Return and try to finish.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if (overflow_state->in_stack_red_zone(addr)) {
|
|
||||||
// Fatal red zone violation. Disable the guard pages and fall through
|
|
||||||
// to handle_unexpected_exception way down below.
|
|
||||||
overflow_state->disable_stack_red_zone();
|
|
||||||
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
|
|
||||||
|
|
||||||
// This is a likely cause, but hard to verify. Let's just print
|
|
||||||
// it as a hint.
|
|
||||||
tty->print_raw_cr("Please check if any of your loaded .so files has "
|
|
||||||
"enabled executable stack (see man page execstack(8))");
|
|
||||||
} else {
|
|
||||||
// Accessing stack address below sp may cause SEGV if current
|
|
||||||
// thread has MAP_GROWSDOWN stack. This should only happen when
|
|
||||||
// current thread was created by user code with MAP_GROWSDOWN flag
|
|
||||||
// and then attached to VM. See notes in os_linux.cpp.
|
|
||||||
if (thread->osthread()->expanding_stack() == 0) {
|
|
||||||
thread->osthread()->set_expanding_stack();
|
|
||||||
if (os::Linux::manually_expand_stack(thread, addr)) {
|
|
||||||
thread->osthread()->clear_expanding_stack();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
thread->osthread()->clear_expanding_stack();
|
|
||||||
} else {
|
|
||||||
fatal("recursive segv. expanding stack.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,43 +148,11 @@ frame os::fetch_frame_from_context(const void* ucVoid) {
|
||||||
return frame(sp, fp, epc);
|
return frame(sp, fp, epc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os::Linux::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) {
|
frame os::fetch_compiled_frame_from_context(const void* ucVoid) {
|
||||||
address pc = (address) os::Linux::ucontext_get_pc(uc);
|
const ucontext_t* uc = (const ucontext_t*)ucVoid;
|
||||||
if (Interpreter::contains(pc)) {
|
|
||||||
// interpreter performs stack banging after the fixed frame header has
|
|
||||||
// been generated while the compilers perform it before. To maintain
|
|
||||||
// semantic consistency between interpreted and compiled frames, the
|
|
||||||
// method returns the Java sender of the current frame.
|
|
||||||
*fr = os::fetch_frame_from_context(uc);
|
|
||||||
if (!fr->is_first_java_frame()) {
|
|
||||||
// get_frame_at_stack_banging_point() is only called when we
|
|
||||||
// have well defined stacks so java_sender() calls do not need
|
|
||||||
// to assert safe_for_sender() first.
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// more complex code with compiled code
|
|
||||||
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
|
|
||||||
CodeBlob* cb = CodeCache::find_blob(pc);
|
|
||||||
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
|
|
||||||
// Not sure where the pc points to, fallback to default
|
|
||||||
// stack overflow handling
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// in compiled code, the stack banging is performed just after the return pc
|
|
||||||
// has been pushed on the stack
|
|
||||||
intptr_t* fp = os::Linux::ucontext_get_fp(uc);
|
intptr_t* fp = os::Linux::ucontext_get_fp(uc);
|
||||||
intptr_t* sp = os::Linux::ucontext_get_sp(uc);
|
intptr_t* sp = os::Linux::ucontext_get_sp(uc);
|
||||||
*fr = frame(sp + 1, fp, (address)*sp);
|
return frame(sp + 1, fp, (address)*sp);
|
||||||
if (!fr->is_java_frame()) {
|
|
||||||
assert(!fr->is_first_frame(), "Safety check");
|
|
||||||
// See java_sender() comment above.
|
|
||||||
*fr = fr->java_sender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(fr->is_java_frame(), "Safety check");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default, gcc always save frame pointer (%ebp/%rbp) on stack. It may get
|
// By default, gcc always save frame pointer (%ebp/%rbp) on stack. It may get
|
||||||
|
@ -324,61 +292,8 @@ JVM_handle_linux_signal(int sig,
|
||||||
// check if fault address is within thread stack
|
// check if fault address is within thread stack
|
||||||
if (thread->is_in_full_stack(addr)) {
|
if (thread->is_in_full_stack(addr)) {
|
||||||
// stack overflow
|
// stack overflow
|
||||||
StackOverflow* overflow_state = thread->stack_overflow_state();
|
if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) {
|
||||||
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
|
return 1; // continue
|
||||||
if (thread->thread_state() == _thread_in_Java) {
|
|
||||||
if (overflow_state->in_stack_reserved_zone(addr)) {
|
|
||||||
frame fr;
|
|
||||||
if (os::Linux::get_frame_at_stack_banging_point(thread, uc, &fr)) {
|
|
||||||
assert(fr.is_java_frame(), "Must be a Java frame");
|
|
||||||
frame activation =
|
|
||||||
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
|
|
||||||
if (activation.sp() != NULL) {
|
|
||||||
overflow_state->disable_stack_reserved_zone();
|
|
||||||
if (activation.is_interpreted_frame()) {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)(
|
|
||||||
activation.fp() + frame::interpreter_frame_initial_sp_offset));
|
|
||||||
} else {
|
|
||||||
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Throw a stack overflow exception. Guard pages will be reenabled
|
|
||||||
// while unwinding the stack.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
|
|
||||||
} else {
|
|
||||||
// Thread was in the vm or native code. Return and try to finish.
|
|
||||||
overflow_state->disable_stack_yellow_reserved_zone();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if (overflow_state->in_stack_red_zone(addr)) {
|
|
||||||
// Fatal red zone violation. Disable the guard pages and fall through
|
|
||||||
// to handle_unexpected_exception way down below.
|
|
||||||
overflow_state->disable_stack_red_zone();
|
|
||||||
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
|
|
||||||
|
|
||||||
// This is a likely cause, but hard to verify. Let's just print
|
|
||||||
// it as a hint.
|
|
||||||
tty->print_raw_cr("Please check if any of your loaded .so files has "
|
|
||||||
"enabled executable stack (see man page execstack(8))");
|
|
||||||
} else {
|
|
||||||
// Accessing stack address below sp may cause SEGV if current
|
|
||||||
// thread has MAP_GROWSDOWN stack. This should only happen when
|
|
||||||
// current thread was created by user code with MAP_GROWSDOWN flag
|
|
||||||
// and then attached to VM. See notes in os_linux.cpp.
|
|
||||||
if (thread->osthread()->expanding_stack() == 0) {
|
|
||||||
thread->osthread()->set_expanding_stack();
|
|
||||||
if (os::Linux::manually_expand_stack(thread, addr)) {
|
|
||||||
thread->osthread()->clear_expanding_stack();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
thread->osthread()->clear_expanding_stack();
|
|
||||||
} else {
|
|
||||||
fatal("recursive segv. expanding stack.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -482,6 +482,7 @@ class os: AllStatic {
|
||||||
|
|
||||||
static address fetch_frame_from_context(const void* ucVoid, intptr_t** sp, intptr_t** fp);
|
static address fetch_frame_from_context(const void* ucVoid, intptr_t** sp, intptr_t** fp);
|
||||||
static frame fetch_frame_from_context(const void* ucVoid);
|
static frame fetch_frame_from_context(const void* ucVoid);
|
||||||
|
static frame fetch_compiled_frame_from_context(const void* ucVoid);
|
||||||
|
|
||||||
static void breakpoint();
|
static void breakpoint();
|
||||||
static bool start_debugging(char *buf, int buflen);
|
static bool start_debugging(char *buf, int buflen);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue