use super::*;
use mach2::exception_types as et;
impl MinidumpWriter {
pub(crate) fn write_exception(
&mut self,
buffer: &mut DumpBuf,
dumper: &TaskDumper,
) -> Result<MDRawDirectory, WriterError> {
let crash_context = self
.crash_context
.as_ref()
.ok_or(WriterError::NoCrashContext)?;
let thread_state = dumper.read_thread_state(crash_context.thread).ok();
let thread_context = if let Some(ts) = &thread_state {
let mut cpu = Default::default();
Self::fill_cpu_context(ts, &mut cpu);
MemoryWriter::alloc_with_val(buffer, cpu)
.map(|mw| mw.location())
.ok()
} else {
None
};
let exception_record = crash_context
.exception
.as_ref()
.map(|exc| {
let code = exc.code as u64;
let wrapped_exc = if exc.kind as u32 == et::EXC_CRASH {
recover_exc_crash_wrapped_exception(code)
} else {
None
};
let exception_code =
if exc.kind as u32 == et::EXC_RESOURCE || exc.kind as u32 == et::EXC_GUARD {
(code >> 32) as u32
} else if let Some(wrapped) = wrapped_exc {
wrapped.code
} else {
code as u32
};
let exception_kind = if let Some(wrapped) = wrapped_exc {
wrapped.kind
} else {
exc.kind
};
let exception_address =
if exception_kind == et::EXC_BAD_ACCESS && exc.subcode.is_some() {
exc.subcode.unwrap_or_default()
} else if let Some(ts) = thread_state {
ts.pc()
} else {
0
};
let mut md_exc = MDException {
exception_code: exception_kind,
exception_flags: exception_code,
exception_address,
..Default::default()
};
md_exc.exception_information[0] = exception_kind as u64;
md_exc.exception_information[1] = code;
md_exc.number_parameters = if let Some(subcode) = exc.subcode {
md_exc.exception_information[2] = subcode;
3
} else {
2
};
md_exc
})
.unwrap_or_default();
let stream = MDRawExceptionStream {
thread_id: crash_context.thread,
exception_record,
thread_context: thread_context.unwrap_or_default(),
__align: 0,
};
let exc_section = MemoryWriter::<MDRawExceptionStream>::alloc_with_val(buffer, stream)?;
Ok(MDRawDirectory {
stream_type: MDStreamType::ExceptionStream as u32,
location: exc_section.location(),
})
}
}
#[inline]
fn is_valid_exc_crash(exc_code: u64) -> bool {
let wrapped = ((exc_code >> 20) & 0xf) as u32;
!(
wrapped == et::EXC_CRASH || wrapped == et::EXC_RESOURCE || wrapped == et::EXC_GUARD || wrapped == et::EXC_CORPSE_NOTIFY
)
}
#[derive(Copy, Clone)]
struct WrappedException {
kind: u32,
code: u32,
_signal: u8,
}
#[inline]
fn recover_exc_crash_wrapped_exception(code: u64) -> Option<WrappedException> {
is_valid_exc_crash(code).then(|| WrappedException {
kind: ((code >> 20) & 0xf) as u32,
code: (code & 0xfffff) as u32,
_signal: ((code >> 24) & 0xff) as u8,
})
}