diff --git a/lldb/include/lldb/Target/AppleArm64ExceptionClass.h b/lldb/include/lldb/Target/AppleArm64ExceptionClass.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Target/AppleArm64ExceptionClass.h @@ -0,0 +1,50 @@ +//===-- AppleArm64ExceptionClass.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TARGET_APPLEARM64EXCEPTIONCLASS_H +#define LLDB_TARGET_APPLEARM64EXCEPTIONCLASS_H + +#include + +namespace lldb_private { + +enum class AppleArm64ExceptionClass : unsigned { +#define APPLE_ARM64_EXCEPTION_CLASS(Name, Code) Name = Code, +#include "AppleArm64ExceptionClass.def" +}; + +/// Get the Apple ARM64 exception class encoded within \p esr. +inline AppleArm64ExceptionClass getAppleArm64ExceptionClass(uint32_t esr) { + /* + * Exception Syndrome Register + * + * 31 26 25 24 0 + * +------+--+------------------+ + * | EC |IL| ISS | + * +------+--+------------------+ + * + * EC - Exception Class + * IL - Instruction Length + * ISS - Instruction Specific Syndrome + */ + return static_cast(esr >> 26); +} + +inline const char *toString(AppleArm64ExceptionClass EC) { + switch (EC) { +#define APPLE_ARM64_EXCEPTION_CLASS(Name, Code) \ + case AppleArm64ExceptionClass::Name: \ + return #Name; +#include "AppleArm64ExceptionClass.def" + } + return "Unknown Exception Class"; +} + +} // namespace lldb_private + +#endif // LLDB_TARGET_APPLEARM64EXCEPTIONCLASS_H diff --git a/lldb/include/lldb/Target/AppleArm64ExceptionClass.def b/lldb/include/lldb/Target/AppleArm64ExceptionClass.def new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Target/AppleArm64ExceptionClass.def @@ -0,0 +1,50 @@ +/*===-- AppleArm64ExceptionClass.def ---------------------------*- C++ -*-=== *\ +|* +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +|* See https://llvm.org/LICENSE.txt for license information. +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +|* +\*===----------------------------------------------------------------------===*/ + +// Defines ESR exception classes for Apple arm64* targets. +// These largely map 1:1 to the exception classes defined in ARM's architecture +// reference manual, but there are some Apple-specific additions. + +#ifndef APPLE_ARM64_EXCEPTION_CLASS +#error "APPLE_ARM64_EXCEPTION_CLASS(Name, Code) not defined." +#endif + +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_UNCATEGORIZED, 0x00) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_WFI_WFE, 0x01) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_MCR_MRC_CP15_TRAP, 0x03) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_MCRR_MRRC_CP15_TRAP, 0x04) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_MCR_MRC_CP14_TRAP, 0x05) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_LDC_STC_CP14_TRAP, 0x06) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_TRAP_SIMD_FP, 0x07) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_PTRAUTH_INSTR_TRAP, 0x09) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_MCRR_MRRC_CP14_TRAP, 0x0c) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_ILLEGAL_INSTR_SET, 0x0e) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_SVC_32, 0x11) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_SVC_64, 0x15) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_MSR_TRAP, 0x18) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_PAC_FAIL, 0x1C) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_IABORT_EL0, 0x20) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_IABORT_EL1, 0x21) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_PC_ALIGN, 0x22) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_DABORT_EL0, 0x24) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_DABORT_EL1, 0x25) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_SP_ALIGN, 0x26) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_FLOATING_POINT_32, 0x28) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_FLOATING_POINT_64, 0x2C) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_SERROR_INTERRUPT, 0x2F) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_BKPT_REG_MATCH_EL0, 0x30) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_BKPT_REG_MATCH_EL1, 0x31) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_SW_STEP_DEBUG_EL0, 0x32) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_SW_STEP_DEBUG_EL1, 0x33) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_WATCHPT_MATCH_EL0, 0x34) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_WATCHPT_MATCH_EL1, 0x35) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_BKPT_AARCH32, 0x38) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_BRK_AARCH64, 0x3C) +APPLE_ARM64_EXCEPTION_CLASS(ESR_EC_PRIV, 0x3F) + +#undef APPLE_ARM64_EXCEPTION_CLASS diff --git a/lldb/include/lldb/module.modulemap b/lldb/include/lldb/module.modulemap --- a/lldb/include/lldb/module.modulemap +++ b/lldb/include/lldb/module.modulemap @@ -119,6 +119,7 @@ requires cplusplus umbrella "Target" + textual header "Target/AppleArm64ExceptionClass.def" module * { export * } } } diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -745,13 +745,14 @@ PrintRegisterValue(reg_ctx, "sp", nullptr, 8, data); PrintRegisterValue(reg_ctx, "pc", nullptr, 8, data); PrintRegisterValue(reg_ctx, "cpsr", nullptr, 4, data); + data.PutHex32(0); // uint32_t pad at the end // Write out the EXC registers - // data.PutHex32 (EXCRegSet); - // data.PutHex32 (EXCWordCount); - // WriteRegister (reg_ctx, "far", NULL, 8, data); - // WriteRegister (reg_ctx, "esr", NULL, 4, data); - // WriteRegister (reg_ctx, "exception", NULL, 4, data); + data.PutHex32(EXCRegSet); + data.PutHex32(EXCWordCount); + PrintRegisterValue(reg_ctx, "far", NULL, 8, data); + PrintRegisterValue(reg_ctx, "esr", NULL, 4, data); + PrintRegisterValue(reg_ctx, "exception", NULL, 4, data); return true; } return false; diff --git a/lldb/source/Plugins/Process/mach-core/ThreadMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ThreadMachCore.cpp --- a/lldb/source/Plugins/Process/mach-core/ThreadMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ThreadMachCore.cpp @@ -9,7 +9,9 @@ #include "ThreadMachCore.h" #include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Host/SafeMachO.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/AppleArm64ExceptionClass.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" @@ -17,6 +19,7 @@ #include "lldb/Target/Unwind.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/State.h" #include "lldb/Utility/StreamString.h" @@ -88,10 +91,55 @@ return reg_ctx_sp; } +static bool IsCrashExceptionClass(AppleArm64ExceptionClass EC) { + switch (EC) { + case AppleArm64ExceptionClass::ESR_EC_UNCATEGORIZED: + case AppleArm64ExceptionClass::ESR_EC_SVC_32: + case AppleArm64ExceptionClass::ESR_EC_SVC_64: + // In the ARM exception model, a process takes an exception when asking the + // kernel to service a system call. Don't treat this like a crash. + return false; + default: + return true; + } +} + bool ThreadMachCore::CalculateStopInfo() { ProcessSP process_sp(GetProcess()); if (process_sp) { - SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, SIGSTOP)); + StopInfoSP stop_info; + RegisterContextSP reg_ctx_sp = GetRegisterContext(); + + if (reg_ctx_sp) { + Target &target = process_sp->GetTarget(); + const ArchSpec arch_spec = target.GetArchitecture(); + const uint32_t cputype = arch_spec.GetMachOCPUType(); + + if (cputype == llvm::MachO::CPU_TYPE_ARM64 || + cputype == llvm::MachO::CPU_TYPE_ARM64_32) { + const RegisterInfo *esr_info = reg_ctx_sp->GetRegisterInfoByName("esr"); + const RegisterInfo *far_info = reg_ctx_sp->GetRegisterInfoByName("far"); + RegisterValue esr, far; + if (reg_ctx_sp->ReadRegister(esr_info, esr) && + reg_ctx_sp->ReadRegister(far_info, far)) { + const uint32_t esr_val = esr.GetAsUInt32(); + const AppleArm64ExceptionClass exception_class = + getAppleArm64ExceptionClass(esr_val); + if (IsCrashExceptionClass(exception_class)) { + StreamString S; + S.Printf("%s (fault address: 0x%" PRIx64 ")", + toString(exception_class), far.GetAsUInt64()); + stop_info = + StopInfo::CreateStopReasonWithException(*this, S.GetData()); + } + } + } + } + + // Set a stop reason for crashing threads only so that they get selected + // preferentially. + if (stop_info) + SetStopInfo(stop_info); return true; } return false; diff --git a/lldb/test/API/macosx/corefile-exception-reason/Makefile b/lldb/test/API/macosx/corefile-exception-reason/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/corefile-exception-reason/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES = main.cpp + +include Makefile.rules diff --git a/lldb/test/API/macosx/corefile-exception-reason/TestCorefileExceptionReason.py b/lldb/test/API/macosx/corefile-exception-reason/TestCorefileExceptionReason.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/corefile-exception-reason/TestCorefileExceptionReason.py @@ -0,0 +1,43 @@ +"""Test that lldb can report the exception reason for threads in a corefile.""" + +import os +import re +import subprocess + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestCorefileExceptionReason(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfOutOfTreeDebugserver # newer debugserver required for these qMemoryRegionInfo types + @no_debug_info_test + @skipUnlessDarwin + @skipIf(archs=no_match(['arm64','arm64e'])) + def test(self): + + corefile = self.getBuildArtifact("process.core") + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.cpp")) + + self.runCmd("continue") + + self.runCmd("process save-core -s stack " + corefile) + process.Kill() + self.dbg.DeleteTarget(target) + + # Now load the corefile + target = self.dbg.CreateTarget('') + process = target.LoadCore(corefile) + thread = process.GetSelectedThread() + self.assertTrue(process.GetSelectedThread().IsValid()) + if self.TraceOn(): + self.runCmd("image list") + self.runCmd("bt") + self.runCmd("fr v") + + self.assertTrue(thread.GetStopDescription(256) == "ESR_EC_DABORT_EL0 (fault address: 0x0)") diff --git a/lldb/test/API/macosx/corefile-exception-reason/main.cpp b/lldb/test/API/macosx/corefile-exception-reason/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/corefile-exception-reason/main.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +void *sleep_worker(void *in) { + sleep(30); + sleep(30); + return nullptr; +} + +void *crash_worker(void *in) { + sleep(1); + volatile int *p = nullptr; // break here + return (void *)*p; +} + +int main() { + std::vector threads; + threads.push_back(std::move(std::thread(crash_worker, nullptr))); + for (int i = 0; i < 15; i++) + threads.push_back(std::move(std::thread(sleep_worker, nullptr))); + sleep(10); +}