Skip to content

[MachCore] Report arm64 thread exception state #3269

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions lldb/include/lldb/Target/AppleArm64ExceptionClass.def
Original file line number Diff line number Diff line change
@@ -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
50 changes: 50 additions & 0 deletions lldb/include/lldb/Target/AppleArm64ExceptionClass.h
Original file line number Diff line number Diff line change
@@ -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 <cstdint>

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<AppleArm64ExceptionClass>(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
1 change: 1 addition & 0 deletions lldb/include/lldb/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ module lldb_Wrapper {
requires cplusplus

umbrella "Target"
textual header "Target/AppleArm64ExceptionClass.def"
module * { export * }
}
}
Expand Down
11 changes: 6 additions & 5 deletions lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,13 +749,14 @@ class RegisterContextDarwin_arm64_Mach : public RegisterContextDarwin_arm64 {
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;
Expand Down
50 changes: 49 additions & 1 deletion lldb/source/Plugins/Process/mach-core/ThreadMachCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@
#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"
#include "lldb/Target/Target.h"
#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"

Expand Down Expand Up @@ -88,10 +91,55 @@ ThreadMachCore::CreateRegisterContextForFrame(StackFrame *frame) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ def test_selected_thread(self):

# Verify that the correct thread is selected
thread = process.GetSelectedThread()
self.assertEqual(thread.GetThreadID(), 0x333333333)
self.assertEqual(thread.GetThreadID(), 0x111111111)
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,25 @@ def get_target(self):

def get_thread_info(self):
if not self.threads:
# FIXME: LLDB is not actually parsing thread stop reasons.
self.threads = [{
'tid': 0x111111111,
'name': 'one',
'queue': 'queue1',
'state': 'stopped',
'stop_reason': 'none'
'stop_reason': 'not parsed'
}, {
'tid': 0x222222222,
'name': 'two',
'queue': 'queue2',
'state': 'stopped',
'stop_reason': 'none'
'stop_reason': 'not parsed'
}, {
'tid': 0x333333333,
'name': 'three',
'queue': 'queue3',
'state': 'stopped',
'stop_reason': 'sigstop',
'stop_reason': 'not parsed - should be "sigstop" though',
'core': 0
}]
return self.threads
3 changes: 3 additions & 0 deletions lldb/test/API/macosx/corefile-exception-reason/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES = main.cpp

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -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)")
24 changes: 24 additions & 0 deletions lldb/test/API/macosx/corefile-exception-reason/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <stdlib.h>
#include <thread>
#include <unistd.h>
#include <vector>

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<std::thread> 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);
}