Add handler for printing java stack traces for compiled code SIGSEGV.
Added a new FaultHandler which attempts to print a java stack trace
when a SIGSEGV occurse in generated code. This should help debugging
compiler and GC related heap corruption.
Bug: 13725693
Bug: 12934910
Change-Id: Id54d83ea180c222eb86d449c61926e83f0b026ad
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index 65a4952..aaba598 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -45,12 +45,13 @@
return instr_size;
}
-void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) {
+void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method,
+ uintptr_t* out_return_pc, uintptr_t* out_sp) {
struct ucontext *uc = (struct ucontext *)context;
struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
- uintptr_t* sp = reinterpret_cast<uint32_t*>(sc->arm_sp);
- LOG(DEBUG) << "sp: " << sp;
- if (sp == nullptr) {
+ *out_sp = static_cast<uintptr_t>(sc->arm_sp);
+ LOG(DEBUG) << "sp: " << *out_sp;
+ if (*out_sp == 0) {
return;
}
@@ -58,12 +59,12 @@
// get the method from the top of the stack. However it's in r0.
uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(sc->fault_address);
uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
- reinterpret_cast<uint8_t*>(sp) - Thread::kStackOverflowReservedBytes);
+ reinterpret_cast<uint8_t*>(*out_sp) - Thread::kStackOverflowReservedBytes);
if (overflow_addr == fault_addr) {
- method = sc->arm_r0;
+ *out_method = reinterpret_cast<mirror::ArtMethod*>(sc->arm_r0);
} else {
// The method is at the top of the stack.
- method = sp[0];
+ *out_method = reinterpret_cast<mirror::ArtMethod*>(reinterpret_cast<uintptr_t*>(*out_sp)[0]);
}
// Work out the return PC. This will be the address of the instruction
@@ -76,7 +77,7 @@
LOG(DEBUG) << "pc: " << std::hex << static_cast<void*>(ptr);
uint32_t instr_size = GetInstructionSize(ptr);
- return_pc = (sc->arm_pc + instr_size) | 1;
+ *out_return_pc = (sc->arm_pc + instr_size) | 1;
}
bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
@@ -87,7 +88,7 @@
// register in order to find the mapping.
// Need to work out the size of the instruction that caused the exception.
- struct ucontext *uc = (struct ucontext *)context;
+ struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc);
diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index 419e5af..74c3023 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -29,7 +29,8 @@
namespace art {
-void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) {
+void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method,
+ uintptr_t* out_return_pc, uintptr_t* out_sp) {
}
bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc
index 8d494c1..1ecd7d9 100644
--- a/runtime/arch/mips/fault_handler_mips.cc
+++ b/runtime/arch/mips/fault_handler_mips.cc
@@ -29,7 +29,8 @@
namespace art {
-void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) {
+void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method,
+ uintptr_t* out_return_pc, uintptr_t* out_sp) {
}
bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index 171a541..7c1980e 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -29,7 +29,8 @@
namespace art {
-void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) {
+void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method,
+ uintptr_t* out_return_pc, uintptr_t* out_sp) {
}
bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
diff --git a/runtime/arch/x86_64/fault_handler_x86_64.cc b/runtime/arch/x86_64/fault_handler_x86_64.cc
index 3ef19fb..233d3c7 100644
--- a/runtime/arch/x86_64/fault_handler_x86_64.cc
+++ b/runtime/arch/x86_64/fault_handler_x86_64.cc
@@ -29,7 +29,8 @@
namespace art {
-void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) {
+void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method,
+ uintptr_t* out_return_pc, uintptr_t* out_sp) {
}
bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index fcb567e..b8093bc 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -60,43 +60,50 @@
}
void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
- bool handled = false;
LOG(DEBUG) << "Handling fault";
- if (IsInGeneratedCode(context)) {
+ if (IsInGeneratedCode(context, true)) {
LOG(DEBUG) << "in generated code, looking for handler";
- for (auto& handler : handlers_) {
+ for (const auto& handler : generated_code_handlers_) {
LOG(DEBUG) << "invoking Action on handler " << handler;
- handled = handler->Action(sig, info, context);
- if (handled) {
+ if (handler->Action(sig, info, context)) {
return;
}
}
}
-
- if (!handled) {
- LOG(ERROR)<< "Caught unknown SIGSEGV in ART fault handler";
- oldaction_.sa_sigaction(sig, info, context);
- }
-}
-
-void FaultManager::AddHandler(FaultHandler* handler) {
- handlers_.push_back(handler);
-}
-
-void FaultManager::RemoveHandler(FaultHandler* handler) {
- for (Handlers::iterator i = handlers_.begin(); i != handlers_.end(); ++i) {
- FaultHandler* h = *i;
- if (h == handler) {
- handlers_.erase(i);
+ for (const auto& handler : other_handlers_) {
+ if (handler->Action(sig, info, context)) {
return;
}
}
+ LOG(ERROR)<< "Caught unknown SIGSEGV in ART fault handler";
+ oldaction_.sa_sigaction(sig, info, context);
}
+void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) {
+ if (generated_code) {
+ generated_code_handlers_.push_back(handler);
+ } else {
+ other_handlers_.push_back(handler);
+ }
+}
+
+void FaultManager::RemoveHandler(FaultHandler* handler) {
+ auto it = std::find(generated_code_handlers_.begin(), generated_code_handlers_.end(), handler);
+ if (it != generated_code_handlers_.end()) {
+ generated_code_handlers_.erase(it);
+ return;
+ }
+ auto it2 = std::find(other_handlers_.begin(), other_handlers_.end(), handler);
+ if (it2 != other_handlers_.end()) {
+ other_handlers_.erase(it);
+ return;
+ }
+ LOG(FATAL) << "Attempted to remove non existent handler " << handler;
+}
// This function is called within the signal handler. It checks that
// the mutator_lock is held (shared). No annotalysis is done.
-bool FaultManager::IsInGeneratedCode(void *context) {
+bool FaultManager::IsInGeneratedCode(void* context, bool check_dex_pc) {
// We can only be running Java code in the current thread if it
// is in Runnable state.
LOG(DEBUG) << "Checking for generated code";
@@ -119,27 +126,25 @@
return false;
}
- uintptr_t potential_method = 0;
+ mirror::ArtMethod* method_obj = 0;
uintptr_t return_pc = 0;
+ uintptr_t sp = 0;
// Get the architecture specific method address and return address. These
- // are in architecture specific files in arch/<arch>/fault_handler_<arch>.cc
- GetMethodAndReturnPC(context, /*out*/potential_method, /*out*/return_pc);
+ // are in architecture specific files in arch/<arch>/fault_handler_<arch>.
+ GetMethodAndReturnPCAndSP(context, &method_obj, &return_pc, &sp);
// If we don't have a potential method, we're outta here.
- LOG(DEBUG) << "potential method: " << potential_method;
- if (potential_method == 0) {
+ LOG(DEBUG) << "potential method: " << method_obj;
+ if (method_obj == 0 || !IsAligned<kObjectAlignment>(method_obj)) {
LOG(DEBUG) << "no method";
return false;
}
// Verify that the potential method is indeed a method.
// TODO: check the GC maps to make sure it's an object.
-
- mirror::Object* method_obj =
- reinterpret_cast<mirror::Object*>(potential_method);
-
// Check that the class pointer inside the object is not null and is aligned.
+ // TODO: Method might be not a heap address, and GetClass could fault.
mirror::Class* cls = method_obj->GetClass<kVerifyNone>();
if (cls == nullptr) {
LOG(DEBUG) << "not a class";
@@ -164,41 +169,64 @@
// We can be certain that this is a method now. Check if we have a GC map
// at the return PC address.
- mirror::ArtMethod* method =
- reinterpret_cast<mirror::ArtMethod*>(potential_method);
if (true || kIsDebugBuild) {
LOG(DEBUG) << "looking for dex pc for return pc " << std::hex << return_pc;
- const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(method);
+ const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(method_obj);
uint32_t sought_offset = return_pc - reinterpret_cast<uintptr_t>(code);
LOG(DEBUG) << "pc offset: " << std::hex << sought_offset;
}
- uint32_t dexpc = method->ToDexPc(return_pc, false);
+ uint32_t dexpc = method_obj->ToDexPc(return_pc, false);
LOG(DEBUG) << "dexpc: " << dexpc;
- return dexpc != DexFile::kDexNoIndex;
+ return !check_dex_pc || dexpc != DexFile::kDexNoIndex;
+}
+
+FaultHandler::FaultHandler(FaultManager* manager) : manager_(manager) {
}
//
// Null pointer fault handler
//
-
-NullPointerHandler::NullPointerHandler(FaultManager* manager) {
- manager->AddHandler(this);
+NullPointerHandler::NullPointerHandler(FaultManager* manager) : FaultHandler(manager) {
+ manager_->AddHandler(this, true);
}
//
// Suspension fault handler
//
-
-SuspensionHandler::SuspensionHandler(FaultManager* manager) {
- manager->AddHandler(this);
+SuspensionHandler::SuspensionHandler(FaultManager* manager) : FaultHandler(manager) {
+ manager_->AddHandler(this, true);
}
//
// Stack overflow fault handler
//
-
-StackOverflowHandler::StackOverflowHandler(FaultManager* manager) {
- manager->AddHandler(this);
+StackOverflowHandler::StackOverflowHandler(FaultManager* manager) : FaultHandler(manager) {
+ manager_->AddHandler(this, true);
}
+
+//
+// Stack trace handler, used to help get a stack trace from SIGSEGV inside of compiled code.
+//
+JavaStackTraceHandler::JavaStackTraceHandler(FaultManager* manager) : FaultHandler(manager) {
+ manager_->AddHandler(this, false);
+}
+
+bool JavaStackTraceHandler::Action(int sig, siginfo_t* siginfo, void* context) {
+ // Make sure that we are in the generated code, but we may not have a dex pc.
+ if (manager_->IsInGeneratedCode(context, false)) {
+ LOG(ERROR) << "Dumping java stack trace for crash in generated code";
+ mirror::ArtMethod* method = nullptr;
+ uintptr_t return_pc = 0;
+ uintptr_t sp = 0;
+ manager_->GetMethodAndReturnPCAndSP(context, &method, &return_pc, &sp);
+ Thread* self = Thread::Current();
+ // Inside of generated code, sp[0] is the method, so sp is the frame.
+ mirror::ArtMethod** frame = reinterpret_cast<mirror::ArtMethod**>(sp);
+ self->SetTopOfStack(frame, 0); // Since we don't necessarily have a dex pc, pass in 0.
+ self->DumpJavaStack(LOG(ERROR));
+ }
+ return false; // Return false since we want to propagate the fault to the main signal handler.
+}
+
} // namespace art
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 9fe6e9a..ea2f7c8 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -26,6 +26,11 @@
#include "base/mutex.h" // For annotalysis.
namespace art {
+
+namespace mirror {
+class ArtMethod;
+} // namespace mirror
+
class FaultHandler;
class FaultManager {
@@ -36,53 +41,77 @@
void Init();
void HandleFault(int sig, siginfo_t* info, void* context);
- void AddHandler(FaultHandler* handler);
+ void AddHandler(FaultHandler* handler, bool generated_code);
void RemoveHandler(FaultHandler* handler);
+ void GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method,
+ uintptr_t* out_return_pc, uintptr_t* out_sp);
+ bool IsInGeneratedCode(void *context, bool check_dex_pc) NO_THREAD_SAFETY_ANALYSIS;
private:
- bool IsInGeneratedCode(void *context) NO_THREAD_SAFETY_ANALYSIS;
- void GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc);
-
- typedef std::vector<FaultHandler*> Handlers;
- Handlers handlers_;
+ std::vector<FaultHandler*> generated_code_handlers_;
+ std::vector<FaultHandler*> other_handlers_;
struct sigaction oldaction_;
+ DISALLOW_COPY_AND_ASSIGN(FaultManager);
};
class FaultHandler {
public:
- FaultHandler() : manager_(nullptr) {}
- explicit FaultHandler(FaultManager* manager) : manager_(manager) {}
+ explicit FaultHandler(FaultManager* manager);
virtual ~FaultHandler() {}
+ FaultManager* GetFaultManager() {
+ return manager_;
+ }
virtual bool Action(int sig, siginfo_t* siginfo, void* context) = 0;
+
protected:
FaultManager* const manager_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FaultHandler);
};
class NullPointerHandler FINAL : public FaultHandler {
public:
- NullPointerHandler() {}
explicit NullPointerHandler(FaultManager* manager);
bool Action(int sig, siginfo_t* siginfo, void* context) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NullPointerHandler);
};
class SuspensionHandler FINAL : public FaultHandler {
public:
- SuspensionHandler() {}
explicit SuspensionHandler(FaultManager* manager);
bool Action(int sig, siginfo_t* siginfo, void* context) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SuspensionHandler);
};
class StackOverflowHandler FINAL : public FaultHandler {
public:
- StackOverflowHandler() {}
explicit StackOverflowHandler(FaultManager* manager);
bool Action(int sig, siginfo_t* siginfo, void* context) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackOverflowHandler);
};
+class JavaStackTraceHandler FINAL : public FaultHandler {
+ public:
+ explicit JavaStackTraceHandler(FaultManager* manager);
+
+ bool Action(int sig, siginfo_t* siginfo, void* context) OVERRIDE NO_THREAD_SAFETY_ANALYSIS;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(JavaStackTraceHandler);
+};
+
+
// Statically allocated so the the signal handler can get access to it.
extern FaultManager fault_manager;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f016189..3e3b5e4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -77,6 +77,7 @@
namespace art {
+static constexpr bool kEnableJavaStackTraceHandler = true;
Runtime* Runtime::instance_ = NULL;
Runtime::Runtime()
@@ -523,12 +524,11 @@
if (options->explicit_checks_ != (ParsedOptions::kExplicitSuspendCheck |
ParsedOptions::kExplicitNullCheck |
- ParsedOptions::kExplicitStackOverflowCheck)) {
- // Initialize the fault manager.
+ ParsedOptions::kExplicitStackOverflowCheck) || kEnableJavaStackTraceHandler) {
fault_manager.Init();
- // These need to be in a specific order. The null point check must be
- // the last in the list.
+ // These need to be in a specific order. The null point check handler must be
+ // after the suspend check and stack overflow check handlers.
if ((options->explicit_checks_ & ParsedOptions::kExplicitSuspendCheck) == 0) {
suspend_handler_ = new SuspensionHandler(&fault_manager);
}
@@ -540,6 +540,10 @@
if ((options->explicit_checks_ & ParsedOptions::kExplicitNullCheck) == 0) {
null_pointer_handler_ = new NullPointerHandler(&fault_manager);
}
+
+ if (kEnableJavaStackTraceHandler) {
+ new JavaStackTraceHandler(&fault_manager);
+ }
}
heap_ = new gc::Heap(options->heap_initial_size_,
diff --git a/runtime/thread.cc b/runtime/thread.cc
index fd5b599..2125ad5 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -830,7 +830,7 @@
int line_number = -1;
if (dex_cache != nullptr) { // be tolerant of bad input
const DexFile& dex_file = *dex_cache->GetDexFile();
- line_number = dex_file.GetLineNumFromPC(m, GetDexPc());
+ line_number = dex_file.GetLineNumFromPC(m, GetDexPc(false));
}
if (line_number == last_line_number && last_method == m) {
++repetition_count;
@@ -903,6 +903,13 @@
return current_method != nullptr && current_method->IsNative();
}
+void Thread::DumpJavaStack(std::ostream& os) const {
+ UniquePtr<Context> context(Context::Create());
+ StackDumpVisitor dumper(os, const_cast<Thread*>(this), context.get(),
+ !tls32_.throwing_OutOfMemoryError);
+ dumper.WalkStack();
+}
+
void Thread::DumpStack(std::ostream& os) const {
// TODO: we call this code when dying but may not have suspended the thread ourself. The
// IsSuspended check is therefore racy with the use for dumping (normally we inhibit
@@ -916,10 +923,7 @@
SirtRef<mirror::ArtMethod> method_ref(Thread::Current(), GetCurrentMethod(nullptr));
DumpNativeStack(os, GetTid(), " native: ", false, method_ref.get());
}
- UniquePtr<Context> context(Context::Create());
- StackDumpVisitor dumper(os, const_cast<Thread*>(this), context.get(),
- !tls32_.throwing_OutOfMemoryError);
- dumper.WalkStack();
+ DumpJavaStack(os);
} else {
os << "Not able to dump stack of thread that isn't suspended";
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 59fe724..f1c1d77 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -156,6 +156,10 @@
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DumpJavaStack(std::ostream& os) const
+ LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Dumps the SIGQUIT per-thread header. 'thread' can be NULL for a non-attached thread, in which
// case we use 'tid' to identify the thread, and we'll include as much information as we can.
static void DumpState(std::ostream& os, const Thread* thread, pid_t tid)