#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Status
{
pub command_name: CommandName,
pub file_mode_creation_mask: mode_t,
pub state: ProcessState,
pub thread_group_identifier: ProcessIdentifier,
pub numa_group_identifier: Option<ProcessIdentifier>,
pub process_identifier: ProcessIdentifier,
pub parent_process_identifier: Option<ProcessIdentifier>,
pub tracer_process_identifier: Option<ProcessIdentifier>,
pub user_identifiers: UserIdentifiers,
pub group_identifiers: GroupIdentifiers,
pub number_of_file_descriptor_slots_currently_allocated: u64,
pub groups: Groups,
pub descendant_namespace_thread_group_identifier: NestedProcessIdentifiers,
pub descendant_namespace_process_identifier: NestedProcessIdentifiers,
pub descendant_namespace_process_group_identifier: NestedProcessGroupIdentifiers,
pub descendant_namespace_session_identifier: NestedProcessGroupIdentifiers,
pub peak_virtual_memory_size: Kilobyte,
pub total_program_size: Kilobyte,
pub locked_memory_size: Kilobyte,
pub pinned_memory_size: Kilobyte,
pub peak_resident_set_size: Kilobyte,
pub resident_set_memory_size: Kilobyte,
pub anonymous_resident_set_memory_size: Kilobyte,
pub resident_set_file_mappings_memory_size: Kilobyte,
pub resident_set_shared_memory_size: Kilobyte,
pub private_data_segments_size: Kilobyte,
pub stack_segments_size: Kilobyte,
pub text_segment_size: Kilobyte,
pub dynamically_loaded_shared_library_size: Kilobyte,
pub page_table_entries_size: Kilobyte,
pub swap_memory_size: Kilobyte,
pub huge_tlb_pages_memory_size: Kilobyte,
pub currently_dumping_core: Option<bool>,
pub threads: u64,
pub signal_queue: SignalQueueStatus,
pub thread_pending_signals: Signals,
pub process_shared_pending_signals: Signals,
pub blocked_signals: Signals,
pub ignored_signals: Signals,
pub caught_signals: Signals,
pub inheritable_capabilities_mask: Capabilities,
pub permitted_capabilities_mask: Capabilities,
pub effective_capabilities_mask: Capabilities,
pub capabilities_bounding_set: Capabilities,
pub ambient_capabilities_set: Capabilities,
pub thread_no_new_privileges_bit: Option<bool>,
pub seccomp_mode: SeccompMode,
pub speculation_store_bypass: Option<SpeculationStoreBypassStatus>,
pub cpus_allowed: HyperThreads,
pub cpus_allowed_list: HyperThreads,
pub numa_nodes_allowed: NumaNodes,
pub numa_nodes_allowed_list: NumaNodes,
pub voluntary_context_switches: u64,
pub involuntary_context_switches: u64,
unrecognised: HashMap<Box<[u8]>, Box<[u8]>>,
}
impl Status
{
#[inline(always)]
pub fn unrecognised_statistic(&self, statistic_name: &[u8]) -> Option<&Box<[u8]>>
{
self.unrecognised.get(statistic_name)
}
#[inline(always)]
pub fn self_status(proc_path: &ProcPath) -> Result<Self, StatusFileParseError>
{
Self::process_status(proc_path, ProcessIdentifierChoice::Current)
}
#[inline(always)]
pub fn process_status(proc_path: &ProcPath, process_identifier: ProcessIdentifierChoice) -> Result<Self, StatusFileParseError>
{
macro_rules! parse
{
($reader: ident, $this: ident, $($proc_status_name: literal => $struct_field: ident @ $parse: ident,)*) =>
{
use self::StatusFileParseError::*;
use self::StatusStatisticParseError::*;
$(
let mut $struct_field: bool = false;
)*
let mut zero_based_line_number = 0;
for line in $reader.split_bytes(b'\n')
{
let mut split = line.split_bytes_n(2, b':');
let statistic_name = split.next().unwrap();
let tab_then_statistic_value = split.next().ok_or(CouldNotParseLine { zero_based_line_number, cause: NoValue })?;
let statistic_value = if likely!(tab_then_statistic_value.starts_with(b"\t"))
{
&tab_then_statistic_value[1 .. ]
}
else
{
return Err(CouldNotParseLine { zero_based_line_number, cause: ValueNotPreceededByHorizontalTab })
};
match statistic_name
{
$(
$proc_status_name => if unlikely!($struct_field)
{
return Err(CouldNotParseLine { zero_based_line_number, cause: DuplicatedStatistic })
}
else
{
$struct_field = true;
let result = Self::$parse(statistic_value).map_err(|cause| CouldNotParseLine { zero_based_line_number, cause})?;
unsafe { write(&mut $this.$struct_field, result) };
},
)*
_ => $this.parse_optional_statistics_and_future_statistics(statistic_name, statistic_value, zero_based_line_number)?,
}
$(
if unlikely!(!$struct_field)
{
return Err(MissingRequiredField(stringify!($struct_field)))
}
)*
zero_based_line_number += 1;
}
}
}
let file_path = proc_path.process_file_path(process_identifier, "status");
let reader = file_path.read_raw()?;
#[allow(deprecated, invalid_value)] let mut this: Self = unsafe_uninitialized();
unsafe { write(&mut this.currently_dumping_core, None) };
unsafe { write(&mut this.thread_no_new_privileges_bit, None) };
unsafe { write(&mut this.speculation_store_bypass, None) };
unsafe { write(&mut this.unrecognised, HashMap::default())};
parse!
(
reader, this,
b"Name" => command_name @ parse_command_name,
b"Umask" => file_mode_creation_mask @ parse_mode,
b"State" => state @ parse_process_state,
b"Tgid" => thread_group_identifier @ parse_process_identifier,
b"Ngid" => numa_group_identifier @ parse_maybe_zero_process_identifier,
b"Pid" => process_identifier @ parse_process_identifier,
b"PPid" => parent_process_identifier @ parse_maybe_zero_process_identifier,
b"TracerPid" => tracer_process_identifier @ parse_maybe_zero_process_identifier,
b"Uid" => user_identifiers @ parse_user_identifiers,
b"Gid" => group_identifiers @ parse_group_identifiers,
b"FDSize" => number_of_file_descriptor_slots_currently_allocated @ parse_u64,
b"Groups" => groups @ parse_groups,
b"NStgid" => descendant_namespace_thread_group_identifier @ parse_process_identifiers,
b"NSpid" => descendant_namespace_process_identifier @ parse_process_identifiers,
b"NSpgid" => descendant_namespace_process_group_identifier @ parse_process_group_identifiers,
b"NSsid" => descendant_namespace_session_identifier @ parse_process_group_identifiers,
b"VmPeak" => peak_virtual_memory_size @ parse_kilobyte,
b"VmSize" => total_program_size @ parse_kilobyte,
b"VmLck" => locked_memory_size @ parse_kilobyte,
b"VmPin" => pinned_memory_size @ parse_kilobyte,
b"VmHWM" => peak_resident_set_size @ parse_kilobyte,
b"VmRSS" => resident_set_memory_size @ parse_kilobyte,
b"RssAnon" => anonymous_resident_set_memory_size @ parse_kilobyte,
b"RssFile" => resident_set_file_mappings_memory_size @ parse_kilobyte,
b"RssShmem" => resident_set_shared_memory_size @ parse_kilobyte,
b"VmData" => private_data_segments_size @ parse_kilobyte,
b"VmStk" => stack_segments_size @ parse_kilobyte,
b"VmExe" => text_segment_size @ parse_kilobyte,
b"VmLi" => dynamically_loaded_shared_library_size @ parse_kilobyte,
b"VmPTE" => page_table_entries_size @ parse_kilobyte,
b"VmSwap" => swap_memory_size @ parse_kilobyte,
b"HugetlbPages" => huge_tlb_pages_memory_size @ parse_kilobyte,
b"Threads" => threads @ parse_u64,
b"SigQ" => signal_queue @ parse_signal_queue,
b"SigPnd" => thread_pending_signals @ parse_signal_bit_set,
b"ShdPnd" => process_shared_pending_signals @ parse_signal_bit_set,
b"SigBlk" => blocked_signals @ parse_signal_bit_set,
b"SigIgn" => ignored_signals @ parse_signal_bit_set,
b"SigCgt" => caught_signals @ parse_signal_bit_set,
b"CapInh" => inheritable_capabilities_mask @ parse_capability_mask_or_set,
b"CapPrm" => permitted_capabilities_mask @ parse_capability_mask_or_set,
b"CapEff" => effective_capabilities_mask @ parse_capability_mask_or_set,
b"CapBnd" => capabilities_bounding_set @ parse_capability_mask_or_set,
b"CapAm" => ambient_capabilities_set @ parse_capability_mask_or_set,
b"Seccomp" => seccomp_mode @ parse_seccomp_mode,
b"Cpus_allowed" => cpus_allowed @ parse_cpus_allowed,
b"Cpus_allowed_list" => cpus_allowed_list @ parse_cpus_allowed_list,
b"Mems_allowed" => numa_nodes_allowed @ parse_numa_nodes_allowed,
b"Mems_allowed_list" => numa_nodes_allowed_list @ parse_numa_nodes_allowed_list,
b"voluntary_ctxt_switches" => voluntary_context_switches @ parse_u64,
b"nonvoluntary_ctxt_switches" => involuntary_context_switches @ parse_u64,
);
if cfg!(debug_assertions)
{
if this.parent_process_identifier.is_none()
{
debug_assert!(!this.process_identifier.should_have_parent())
}
else
{
debug_assert!(this.process_identifier.should_have_parent())
}
}
debug_assert!(!this.cpus_allowed.is_empty());
debug_assert_eq!(this.cpus_allowed, this.cpus_allowed_list);
debug_assert_eq!(this.numa_nodes_allowed, this.numa_nodes_allowed_list);
this.unrecognised.shrink_to_fit();
Ok(this)
}
#[inline(always)]
fn parse_optional_statistics_and_future_statistics(&mut self, statistic_name: &[u8], statistic_value: &[u8], zero_based_line_number: usize) -> Result<(), StatusFileParseError>
{
use self::StatusFileParseError::*;
use self::StatusStatisticParseError::DuplicatedStatistic;
#[inline(always)]
fn parse_optional_statistic<S>(statistic: &mut Option<S>, statistic_value: &[u8], zero_based_line_number: usize, parse: impl FnOnce(&[u8]) -> Result<S, StatusStatisticParseError>) -> Result<(), StatusFileParseError>
{
if unlikely!(statistic.is_some())
{
Err(CouldNotParseLine { zero_based_line_number, cause: DuplicatedStatistic })
}
else
{
let result = parse(statistic_value).map_err(|cause| CouldNotParseLine { zero_based_line_number, cause})?;
*statistic = Some(result);
Ok(())
}
}
match statistic_name
{
b"CoreDumping" => parse_optional_statistic(&mut self.currently_dumping_core, statistic_value, zero_based_line_number, Self::parse_bool),
b"NoNewPrivs" => parse_optional_statistic(&mut self.thread_no_new_privileges_bit, statistic_value, zero_based_line_number, Self::parse_bool),
b"Speculation_Store_Bypass" => parse_optional_statistic(&mut self.speculation_store_bypass, statistic_value, zero_based_line_number, Self::parse_speculation_store_bypass),
_ => self.insert_unrecognised(statistic_name, statistic_value, zero_based_line_number),
}
}
#[inline(always)]
fn insert_unrecognised(&mut self, statistic_name: &[u8], statistic_value: &[u8], zero_based_line_number: usize) -> Result<(), StatusFileParseError>
{
let previous = self.unrecognised.insert(Self::to_box(statistic_name), Self::to_box(statistic_value));
if unlikely!(previous.is_some())
{
return Err(StatusFileParseError::CouldNotParseLine { zero_based_line_number, cause: StatusStatisticParseError::DuplicatedStatistic })
}
Ok(())
}
#[inline(always)]
fn to_box(value: &[u8]) -> Box<[u8]>
{
value.to_vec().into_boxed_slice()
}
#[inline(always)]
fn parse_mode(value: &[u8]) -> Result<mode_t, StatusStatisticParseError>
{
Ok(mode_t::parse_octal_number_fixed_width(value, 4)?)
}
#[inline(always)]
fn parse_command_name(value: &[u8]) -> Result<CommandName, StatusStatisticParseError>
{
Ok(CommandName::from_bytes(value)?)
}
#[inline(always)]
fn parse_process_state(value: &[u8]) -> Result<ProcessState, StatusStatisticParseError>
{
Ok(ProcessState::from_bytes(value)?)
}
#[inline(always)]
fn parse_process_identifier(value: &[u8]) -> Result<ProcessIdentifier, StatusStatisticParseError>
{
Ok(ProcessIdentifier::parse_decimal_number(value)?)
}
#[inline(always)]
fn parse_maybe_zero_process_identifier(value: &[u8]) -> Result<Option<ProcessIdentifier>, StatusStatisticParseError>
{
Ok(Option::<ProcessIdentifier>::parse_decimal_number(value)?)
}
#[inline(always)]
fn parse_user_identifiers(value: &[u8]) -> Result<UserIdentifiers, StatusStatisticParseError>
{
UserIdentifiers::from_bytes(value)
}
#[inline(always)]
fn parse_group_identifiers(value: &[u8]) -> Result<GroupIdentifiers, StatusStatisticParseError>
{
GroupIdentifiers::from_bytes(value)
}
#[inline(always)]
fn parse_groups(value: &[u8]) -> Result<Groups, StatusStatisticParseError>
{
Groups::from_bytes(value)
}
#[inline(always)]
fn parse_process_identifiers(value: &[u8]) -> Result<NestedProcessIdentifiers, StatusStatisticParseError>
{
NestedProcessIdentifiers::from_bytes(value)
}
#[inline(always)]
fn parse_process_group_identifiers(value: &[u8]) -> Result<NestedProcessGroupIdentifiers, StatusStatisticParseError>
{
NestedProcessGroupIdentifiers::from_bytes(value)
}
#[inline(always)]
fn parse_u64(value: &[u8]) -> Result<u64, StatusStatisticParseError>
{
Ok(u64::parse_decimal_number(value)?)
}
#[inline(always)]
fn parse_kilobyte(value: &[u8]) -> Result<Kilobyte, StatusStatisticParseError>
{
const Ending: &'static [u8] = b" kB";
if likely!(value.ends_with(b" kB"))
{
Self::parse_u64(&value[0 .. value.len() - Ending.len()])
}
else
{
Err(StatusStatisticParseError::InvalidEnding)
}
}
#[inline(always)]
fn parse_signal_queue(value: &[u8]) -> Result<SignalQueueStatus, StatusStatisticParseError>
{
SignalQueueStatus::from_bytes(value)
}
#[inline(always)]
fn parse_hexadecimal_u64(value: &[u8]) -> Result<u64, StatusStatisticParseError>
{
Ok(u64::parse_hexadecimal_number_lower_case(value)?)
}
#[inline(always)]
fn parse_signal_bit_set(value: &[u8]) -> Result<Signals, StatusStatisticParseError>
{
Ok(Signals(BitSet::new_from_u64(Self::parse_hexadecimal_u64(value)?)))
}
#[inline(always)]
fn parse_capability_mask_or_set(value: &[u8]) -> Result<Capabilities, StatusStatisticParseError>
{
Ok(Capabilities(BitSet::new_from_u64(Self::parse_hexadecimal_u64(value)?)))
}
#[inline(always)]
fn parse_bool(value: &[u8]) -> Result<bool, StatusStatisticParseError>
{
if likely!(value.len() == 1)
{
match value[0]
{
b'0' => Ok(false),
b'1' => Ok(true),
_ => Err(StatusStatisticParseError::OutOfRange)
}
}
else
{
Err(StatusStatisticParseError::InvalidLength)
}
}
#[inline(always)]
fn parse_seccomp_mode(value: &[u8]) -> Result<SeccompMode, StatusStatisticParseError>
{
SeccompMode::from_bytes(value)
}
#[inline(always)]
fn parse_speculation_store_bypass(value: &[u8]) -> Result<SpeculationStoreBypassStatus, StatusStatisticParseError>
{
SpeculationStoreBypassStatus::from_bytes(value)
}
#[inline(always)]
fn parse_cpus_allowed(value: &[u8]) -> Result<HyperThreads, StatusStatisticParseError>
{
Ok(HyperThreads(BitSet::parse_comma_separated_bit_set(&value)))
}
#[inline(always)]
fn parse_numa_nodes_allowed(value: &[u8]) -> Result<NumaNodes, StatusStatisticParseError>
{
Ok(NumaNodes(BitSet::parse_comma_separated_bit_set(&value)))
}
#[inline(always)]
fn parse_cpus_allowed_list(value: &[u8]) -> Result<HyperThreads, StatusStatisticParseError>
{
Ok(HyperThreads(BitSet::<HyperThread>::parse_linux_list_string(value)?))
}
#[inline(always)]
fn parse_numa_nodes_allowed_list(value: &[u8]) -> Result<NumaNodes, StatusStatisticParseError>
{
Ok(NumaNodes(BitSet::<NumaNode>::parse_linux_list_string(value)?))
}
}