use mach2::exception_types::EXC_RESOURCE;
use std::time::Duration;
pub enum ResourceException {
Cpu(CpuResourceException),
Wakeups(WakeupsResourceException),
Memory(MemoryResourceException),
Io(IoResourceException),
Threads(ThreadsResourceException),
Ports(PortsResourceException),
Unknown { kind: u8, flavor: u8 },
}
#[derive(Copy, Clone, Debug)]
pub enum Flavor<T: Copy + Clone + std::fmt::Debug> {
Known(T),
Unknown(u8),
}
impl<T: TryFrom<u8> + Copy + Clone + std::fmt::Debug> From<u64> for Flavor<T> {
#[inline]
fn from(code: u64) -> Self {
let flavor = resource_exc_flavor(code);
if let Ok(known) = T::try_from(flavor) {
Self::Known(known)
} else {
Self::Unknown(flavor)
}
}
}
impl<T: PartialEq + Copy + Clone + std::fmt::Debug> PartialEq<T> for Flavor<T> {
fn eq(&self, o: &T) -> bool {
match self {
Self::Known(flavor) => flavor == o,
Self::Unknown(_) => false,
}
}
}
#[inline]
pub fn resource_exc_kind(code: u64) -> u8 {
((code >> 61) & 0x7) as u8
}
#[inline]
pub fn resource_exc_flavor(code: u64) -> u8 {
((code >> 58) & 0x7) as u8
}
impl super::ExceptionInfo {
pub fn resource_exception(&self) -> Option<ResourceException> {
if self.kind != EXC_RESOURCE {
return None;
}
let kind = resource_exc_kind(self.code);
let res_exc = if kind == ResourceKind::Cpu as u8 {
ResourceException::Cpu(CpuResourceException::from_exc_info(self.code, self.subcode))
} else if kind == ResourceKind::Wakeups as u8 {
ResourceException::Wakeups(WakeupsResourceException::from_exc_info(
self.code,
self.subcode,
))
} else if kind == ResourceKind::Memory as u8 {
ResourceException::Memory(MemoryResourceException::from_exc_info(self.code))
} else if kind == ResourceKind::Io as u8 {
ResourceException::Io(IoResourceException::from_exc_info(self.code, self.subcode))
} else if kind == ResourceKind::Threads as u8 {
ResourceException::Threads(ThreadsResourceException::from_exc_info(self.code))
} else if kind == ResourceKind::Ports as u8 {
ResourceException::Ports(PortsResourceException::from_exc_info(self.code))
} else {
ResourceException::Unknown {
kind,
flavor: resource_exc_flavor(self.code),
}
};
Some(res_exc)
}
}
#[repr(u8)]
pub enum ResourceKind {
Cpu = 1,
Wakeups = 2,
Memory = 3,
Io = 4,
Threads = 5,
Ports = 6,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum CpuFlavor {
Monitor = 1,
MonitorFatal = 2,
}
impl TryFrom<u8> for CpuFlavor {
type Error = ();
fn try_from(flavor: u8) -> Result<Self, Self::Error> {
match flavor {
1 => Ok(Self::Monitor),
2 => Ok(Self::MonitorFatal),
_ => Err(()),
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct CpuResourceException {
pub flavor: Flavor<CpuFlavor>,
pub is_fatal: bool,
pub observation_interval: Duration,
pub limit: u8,
pub consumed: u8,
}
impl CpuResourceException {
#[inline]
pub fn from_exc_info(code: u64, subcode: Option<u64>) -> Self {
debug_assert_eq!(resource_exc_kind(code), ResourceKind::Cpu as u8);
let flavor = Flavor::from(code);
let interval_seconds = (code >> 7) & 0x1ffffff;
let limit = (code & 0x7f) as u8;
let consumed = subcode.map_or(0, |sc| sc & 0x7f) as u8;
Self {
flavor,
is_fatal: flavor == CpuFlavor::MonitorFatal,
observation_interval: Duration::from_secs(interval_seconds),
limit,
consumed,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum WakeupsFlavor {
Monitor = 1,
}
impl TryFrom<u8> for WakeupsFlavor {
type Error = ();
fn try_from(flavor: u8) -> Result<Self, Self::Error> {
match flavor {
1 => Ok(Self::Monitor),
_ => Err(()),
}
}
}
pub struct WakeupsResourceException {
pub flavor: Flavor<WakeupsFlavor>,
pub observation_interval: Duration,
pub permitted: u32,
pub observed: u32,
}
impl WakeupsResourceException {
#[inline]
pub fn from_exc_info(code: u64, subcode: Option<u64>) -> Self {
debug_assert_eq!(resource_exc_kind(code), ResourceKind::Wakeups as u8);
let flavor = Flavor::from(code);
let interval_seconds = (code >> 20) & 0xfff;
let permitted = (code & 0xfffff) as u32;
let observed = subcode.map_or(0, |sc| sc & 0xfffff) as u32;
Self {
flavor,
observation_interval: Duration::from_secs(interval_seconds),
permitted,
observed,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum MemoryFlavor {
HighWatermark = 1,
}
impl TryFrom<u8> for MemoryFlavor {
type Error = ();
fn try_from(flavor: u8) -> Result<Self, Self::Error> {
match flavor {
1 => Ok(Self::HighWatermark),
_ => Err(()),
}
}
}
pub struct MemoryResourceException {
pub flavor: Flavor<MemoryFlavor>,
pub limit_mib: u16,
}
impl MemoryResourceException {
#[inline]
pub fn from_exc_info(code: u64) -> Self {
debug_assert_eq!(resource_exc_kind(code), ResourceKind::Memory as u8);
let flavor = Flavor::from(code);
let limit_mib = (code & 0x1fff) as u16;
Self { flavor, limit_mib }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum IoFlavor {
PhysicalWrites = 1,
LogicalWrites = 2,
}
impl TryFrom<u8> for IoFlavor {
type Error = ();
fn try_from(flavor: u8) -> Result<Self, Self::Error> {
match flavor {
1 => Ok(Self::PhysicalWrites),
2 => Ok(Self::LogicalWrites),
_ => Err(()),
}
}
}
pub struct IoResourceException {
pub flavor: Flavor<MemoryFlavor>,
pub observation_interval: Duration,
pub limit_mib: u16,
pub observed_mib: u16,
}
impl IoResourceException {
#[inline]
pub fn from_exc_info(code: u64, subcode: Option<u64>) -> Self {
debug_assert_eq!(resource_exc_kind(code), ResourceKind::Io as u8);
let flavor = Flavor::from(code);
let interval_seconds = (code >> 15) & 0x1ffff;
let limit_mib = (code & 0x7fff) as u16;
let observed_mib = subcode.map_or(0, |sc| sc & 0x7fff) as u16;
Self {
flavor,
observation_interval: Duration::from_secs(interval_seconds),
limit_mib,
observed_mib,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum ThreadsFlavor {
HighWatermark = 1,
}
impl TryFrom<u8> for ThreadsFlavor {
type Error = ();
fn try_from(flavor: u8) -> Result<Self, Self::Error> {
match flavor {
1 => Ok(Self::HighWatermark),
_ => Err(()),
}
}
}
pub struct ThreadsResourceException {
pub flavor: Flavor<ThreadsFlavor>,
pub limit: u16,
}
impl ThreadsResourceException {
#[inline]
pub fn from_exc_info(code: u64) -> Self {
debug_assert_eq!(resource_exc_kind(code), ResourceKind::Threads as u8);
let flavor = Flavor::from(code);
let limit = (code & 0x7fff) as u16;
Self { flavor, limit }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum PortsFlavor {
SpaceFull = 1,
}
impl TryFrom<u8> for PortsFlavor {
type Error = ();
fn try_from(flavor: u8) -> Result<Self, Self::Error> {
match flavor {
1 => Ok(Self::SpaceFull),
_ => Err(()),
}
}
}
pub struct PortsResourceException {
pub flavor: Flavor<ThreadsFlavor>,
pub allocated: u32,
}
impl PortsResourceException {
#[inline]
pub fn from_exc_info(code: u64) -> Self {
debug_assert_eq!(resource_exc_kind(code), ResourceKind::Ports as u8);
let flavor = Flavor::from(code);
let allocated = (code & 0xffffff) as u32;
Self { flavor, allocated }
}
}