diff options
-rw-r--r-- | runtime/arch/arm/fault_handler_arm.cc | 19 | ||||
-rw-r--r-- | runtime/arch/arm64/fault_handler_arm64.cc | 3 | ||||
-rw-r--r-- | runtime/arch/mips/fault_handler_mips.cc | 3 | ||||
-rw-r--r-- | runtime/arch/x86/fault_handler_x86.cc | 3 | ||||
-rw-r--r-- | runtime/arch/x86_64/fault_handler_x86_64.cc | 3 | ||||
-rw-r--r-- | runtime/fault_handler.cc | 110 | ||||
-rw-r--r-- | runtime/fault_handler.h | 51 | ||||
-rw-r--r-- | runtime/runtime.cc | 12 | ||||
-rw-r--r-- | runtime/thread.cc | 14 | ||||
-rw-r--r-- | runtime/thread.h | 4 |
10 files changed, 148 insertions, 74 deletions
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 @@ static uint32_t GetInstructionSize(uint8_t* pc) { 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 @@ void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintpt // 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 @@ void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintpt 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 @@ bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { // 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::Init() { } 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); + 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) { - handlers_.push_back(handler); +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) { - for (Handlers::iterator i = handlers_.begin(); i != handlers_.end(); ++i) { - FaultHandler* h = *i; - if (h == handler) { - handlers_.erase(i); - return; - } + 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 @@ bool FaultManager::IsInGeneratedCode(void *context) { 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 @@ bool FaultManager::IsInGeneratedCode(void *context) { // 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) : 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); +} -StackOverflowHandler::StackOverflowHandler(FaultManager* manager) { - manager->AddHandler(this); +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 @@ class FaultManager { 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 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { 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 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { 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 @@ struct StackDumpVisitor : public StackVisitor { 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 @@ static bool ShouldShowNativeStack(const Thread* thread) 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 @@ void Thread::DumpStack(std::ostream& os) const { 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 @@ class Thread { 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) |