//! Contains types and helpers for dealing with `EXC_RESOURCE` exceptions. //! //! `EXC_RESOURCE` exceptions embed details about the resource and the limits //! it exceeded within the `code` and, in some cases `subcode`, fields of the exception //! //! See <https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/osfmk/kern/exc_resource.h> //! for the various constants and decoding of exception information wrapped in //! this module.
use mach2::exception_types::EXC_RESOURCE; use std::time::Duration;
/// The details for an `EXC_RESOURCE` exception as retrieved from the exception's /// code and subcode pubenum ResourceException { /// This is sent by the kernel when the CPU usage monitor is tripped. Possibly fatal.
Cpu(CpuResourceException), /// This is sent by the kernel when the platform idle wakeups monitor is tripped. Possibly fatal.
Wakeups(WakeupsResourceException), /// This is sent by the kernel when a task crosses its high watermark memory limit. Never fatal at least on current MacOS versions.
Memory(MemoryResourceException), /// This is sent by the kernel when a task crosses its I/O limits. Never fatal.
Io(IoResourceException), /// This is sent by the kernel when a task crosses its thread limit. Always fatal.
Threads(ThreadsResourceException), /// This is sent by the kernel when the process is leaking ipc ports and has /// filled its port space. Always fatal.
Ports(PortsResourceException), /// An unknown resource kind due to an addition to the set of possible /// resource exception kinds in exc_resource.h
Unknown { kind: u8, flavor: u8 },
}
/// Each different resource exception type has 1 or more flavors that it can be, /// and while these most likely don't change often, we try to be forward /// compatible by not failing if a particular flavor is unknown #[derive(Copy, Clone, Debug)] pubenum Flavor<T: Copy + Clone + std::fmt::Debug> {
Known(T),
Unknown(u8),
}
/// Retrieves the resource exception kind from an exception code #[inline] pubfn resource_exc_kind(code: u64) -> u8 {
((code >> 61) & 0x7) as u8
}
/// Retrieves the resource exception flavor from an exception code #[inline] pubfn resource_exc_flavor(code: u64) -> u8 {
((code >> 58) & 0x7) as u8
}
implsuper::ExceptionInfo { /// If this is an `EXC_RESOURCE` exception, retrieves the exception metadata /// from the code, otherwise returns `None` pubfn resource_exception(&self) -> Option<ResourceException> { ifself.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))
} elseif kind == ResourceKind::Wakeups as u8 {
ResourceException::Wakeups(WakeupsResourceException::from_exc_info( self.code, self.subcode,
))
} elseif kind == ResourceKind::Memory as u8 {
ResourceException::Memory(MemoryResourceException::from_exc_info(self.code))
} elseif kind == ResourceKind::Io as u8 {
ResourceException::Io(IoResourceException::from_exc_info(self.code, self.subcode))
} elseif kind == ResourceKind::Threads as u8 {
ResourceException::Threads(ThreadsResourceException::from_exc_info(self.code))
} elseif 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)
}
}
/// The types of resources that an `EXC_RESOURCE` exception can pertain to #[repr(u8)] pubenum ResourceKind {
Cpu = 1,
Wakeups = 2,
Memory = 3,
Io = 4,
Threads = 5,
Ports = 6,
}
/// The flavors for a [`CpuResourceException`] #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u8)] pubenum CpuFlavor { /// The process has surpassed its CPU limit
Monitor = 1, /// The process has surpassed its CPU limit, and the process has been configured /// to make this exception fatal
MonitorFatal = 2,
}
/// These exceptions _may_ be fatal. They are not fatal by default at task /// creation but can be made fatal by calling `proc_rlimit_control` with /// `RLIMIT_CPU_USAGE_MONITOR` as the second argument and `CPUMON_MAKE_FATAL` /// set in the flags. The flavor extracted from the exception code determines if /// the exception is fatal. /// /// [Kernel code](https://github.com/apple-oss-distributions/xnu/blob/e7776783b89a353188416a9a346c6cdb4928faad/osfmk/kern/thread.c#L2475-L2616) #[derive(Copy, Clone, Debug)] pubstruct CpuResourceException { pub flavor: Flavor<CpuFlavor>, /// If the exception is fatal. Currently only true if the flavor is [`CpuFlavor::MonitorFatal`] pub is_fatal: bool, /// The time period in which the CPU limit was surpassed pub observation_interval: Duration, /// The CPU % limit pub limit: u8, /// The CPU % consumed by the task pub consumed: 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;
// The default is that cpu resource exceptions are not fatal, so // we only check the flavor against the (currently) one known value // that indicates the exception is fatal Self {
flavor,
is_fatal: flavor == CpuFlavor::MonitorFatal,
observation_interval: Duration::from_secs(interval_seconds),
limit,
consumed,
}
}
}
/// The flavors for a [`WakeupsResourceException`] #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u8)] pubenum WakeupsFlavor {
Monitor = 1,
}
impl TryFrom<u8> for WakeupsFlavor { type Error = ();
let flavor = Flavor::from(code); // Note that Apple has a bug in exc_resource.h where the masks in the // decode macros for the interval and the permitted wakeups have been swapped let interval_seconds = (code >> 20) & 0xfff; let permitted = (code & 0xfffff) as u32; let observed = subcode.map_or(0, |sc| sc & 0xfffff) as u32;
/// These exceptions, as of this writing, are never fatal. /// /// While memory exceptions _can_ be fatal, this appears to only be possible if /// the kernel is built with `CONFIG_JETSAM` or in `DEVELOPMENT` or `DEBUG` modes, /// so as of now, they should never be considered fatal, at least on `MacOS` /// /// [Kernel source](https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/osfmk/kern/task.c#L6767-L6874) pubstruct MemoryResourceException { pub flavor: Flavor<MemoryFlavor>, /// The limit in MiB of the high watermark pub limit_mib: u16,
}
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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.