use std::io;
use crate::IoBackend;
use crate::rt::counters::{KINDS, kind_index};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Stats {
pub shards: Vec<ShardStats>,
pub blocking_threads: usize,
pub blocking_queue_depth: usize,
pub off_shard_errors: ErrorCounts,
pub receive_buffers_missing: u64,
pub completions_dropped: u64,
pub operations_given_up: u64,
pub write_behind_lost: u64,
pub tasks_left: u64,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct ShardStats {
pub worker: Option<usize>,
pub runtime: Option<crate::runtime::RuntimeId>,
pub backend: IoBackend,
pub tasks: usize,
pub polls: u64,
pub parks: u64,
pub parked: std::time::Duration,
pub busy: std::time::Duration,
pub queue_depth: usize,
pub remote_wakes: u64,
pub ops_in_flight: usize,
pub cq_overflows: u64,
pub buffer_bytes: usize,
pub buffer_cap: usize,
pub buffer_exhausted: u64,
pub timers: usize,
pub handoffs: u64,
pub handoffs_failed: u64,
pub longest_poll: std::time::Duration,
pub budget_yields: u64,
pub poll_times: Option<Histogram>,
#[cfg(feature = "io-stats")]
pub io_latencies: Option<IoLatencies>,
#[cfg(feature = "io-stats")]
pub io: Option<IoCounts>,
pub errors: ErrorCounts,
}
#[cfg(feature = "io-stats")]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct IoCounts {
pub reads: u64,
pub writes: u64,
pub bytes_read: u64,
pub bytes_written: u64,
pub accepts: u64,
}
#[cfg(feature = "io-stats")]
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct IoLatencies {
pub reads: Histogram,
pub writes: Histogram,
pub accepts: Histogram,
pub connects: Histogram,
pub files: Histogram,
}
pub(crate) const BUCKETS: usize = 64;
pub(crate) fn bucket_of(nanos: u64) -> usize {
(u64::BITS - 1 - (nanos | 1).leading_zeros()) as usize
}
#[derive(Clone, PartialEq, Eq)]
pub struct Histogram {
pub(crate) buckets: [u64; BUCKETS],
}
impl Histogram {
pub fn count(&self) -> u64 {
self.buckets.iter().sum()
}
pub fn buckets(
&self,
) -> impl Iterator<Item = (std::time::Duration, std::time::Duration, u64)> + '_ {
self.buckets
.iter()
.enumerate()
.filter(|&(_, &n)| n > 0)
.map(|(i, &n)| {
let low = if i == 0 { 0 } else { 1u64 << i };
let high = 1u64.checked_shl(i as u32 + 1).unwrap_or(u64::MAX);
(
std::time::Duration::from_nanos(low),
std::time::Duration::from_nanos(high),
n,
)
})
}
}
impl core::fmt::Debug for Histogram {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(self.buckets()).finish()
}
}
#[derive(Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ErrorCounts {
pub(crate) by_kind: [u64; KINDS.len()],
pub refused_registrations: u64,
pub evictions_given_up: u64,
pub accepts_out_of_descriptors: u64,
pub accepts_skipped: u64,
pub cut_datagrams_dropped: u64,
}
impl ErrorCounts {
pub fn get(&self, kind: io::ErrorKind) -> u64 {
self.by_kind[kind_index(kind)]
}
pub fn iter(&self) -> impl Iterator<Item = (io::ErrorKind, u64)> + '_ {
KINDS
.iter()
.copied()
.zip(self.by_kind.iter().copied())
.filter(|&(_, n)| n > 0)
}
}
impl Default for ErrorCounts {
fn default() -> Self {
Self {
by_kind: [0; KINDS.len()],
refused_registrations: 0,
evictions_given_up: 0,
accepts_out_of_descriptors: 0,
accepts_skipped: 0,
cut_datagrams_dropped: 0,
}
}
}
impl core::fmt::Debug for ErrorCounts {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ErrorCounts")
.field("by_kind", &self.iter().collect::<Vec<_>>())
.field("refused_registrations", &self.refused_registrations)
.field("evictions_given_up", &self.evictions_given_up)
.field(
"accepts_out_of_descriptors",
&self.accepts_out_of_descriptors,
)
.field("accepts_skipped", &self.accepts_skipped)
.field("cut_datagrams_dropped", &self.cut_datagrams_dropped)
.finish()
}
}
pub fn stats() -> Stats {
crate::rt::counters::snapshot()
}