1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
use crate::c_headers;
use crate::taskstats;
use std::mem;
use std::time::Duration;
// https://stackoverflow.com/questions/53619695/calculating-maximum-value-of-a-set-of-constant-expressions-at-compile-time
const fn const_max(a: usize, b: usize) -> usize {
[a, b][(a < b) as usize]
}
pub const TASKSTATS_SIZE: usize = const_max(
mem::size_of::<taskstats>(),
mem::size_of::<c_headers::taskstats>(),
);
/// The taskstats representation for a task.
/// This struct remaps commonly used `struct taskstats` fields for primarily:
/// * Access values with rust's primitive types
/// * Better structured organization of group of fields
/// * Support serialization
///
/// There are more (but may not much interested) fields in the original
/// `struct taskstats` and they are accessible through obtaining the original
/// struct by `TaskStats#inner()`.
#[derive(Clone, Copy)]
pub struct TaskStats {
pub(crate) inner_buf: [u8; TASKSTATS_SIZE],
/// The target task ID
pub tid: u32,
/// Staticstics related to CPU time
pub cpu: Cpu,
/// Statistics related to memory, vm
pub memory: Memory,
/// Staticstics related to I/O at syscall surface
pub io: Io,
/// Statistics related to I/O at block device level
pub blkio: BlkIo,
/// Statistics related to context switches
pub ctx_switches: ContextSwitches,
/// Statistics related to scheduling delay (delay accounting)
pub delays: Delays,
}
/// Staticstics related to CPU time
#[derive(Debug, Clone, Copy)]
pub struct Cpu {
/// User CPU time
pub utime_total: Duration,
/// System CPU time
pub stime_total: Duration,
/// Wall-clock running time
pub real_time_total: Duration,
/// Virtual running time
pub virtual_time_total: Duration,
}
/// Statistics related to memory, vm
#[derive(Debug, Clone, Copy)]
pub struct Memory {
/// Accumulated RSS usage in duration of a task, in MBytes-usecs
pub rss_total: u64,
/// Accumulated virtual memory usage in duration of a task
pub virt_total: u64,
/// Minor faults count
pub minor_faults: u64,
/// Major faults count
pub major_faults: u64,
}
/// Staticstics related to I/O at syscall surface
#[derive(Debug, Clone, Copy)]
pub struct Io {
/// Bytes read
pub read_bytes: u64,
/// Bytes written
pub write_bytes: u64,
/// Number of read syscalls
pub read_syscalls: u64,
/// Number of write syscalls
pub write_syscalls: u64,
}
/// Statistics related to I/O at block device level
#[derive(Debug, Clone, Copy)]
pub struct BlkIo {
/// Bytes read
pub read_bytes: u64,
/// Bytes written
pub write_bytes: u64,
/// Bytes of cancelled writes
pub cancelled_write_bytes: u64,
}
/// Statistics related to context switches
#[derive(Debug, Clone, Copy)]
pub struct ContextSwitches {
/// Count of voluntary context switches
pub voluntary: u64,
/// Count of non-voluntary context switches
pub non_voluntary: u64,
}
/// Statistics related to scheduling delay (delay accounting)
#[derive(Debug, Clone, Copy)]
pub struct Delays {
/// Delay waiting for cpu, while runnable
pub cpu: DelayStat,
/// Delay waiting for synchronous block I/O to complete
pub blkio: DelayStat,
/// Delay waiting for page fault I/O (swap in only)
pub swapin: DelayStat,
/// Delay waiting for memory reclaim
pub freepages: DelayStat,
}
#[derive(Debug, Clone, Copy)]
pub struct DelayStat {
/// Number of delay values recorded
pub count: u64,
/// Cumulative total delay
pub delay_total: Duration,
}
impl From<&[u8]> for TaskStats {
fn from(buf: &[u8]) -> Self {
let mut inner_buf = [0u8; TASKSTATS_SIZE];
inner_buf.copy_from_slice(&buf[..TASKSTATS_SIZE]);
let ts = unsafe { &*(inner_buf.as_ptr() as *const _ as *const taskstats) };
TaskStats {
tid: ts.ac_pid,
cpu: Cpu {
utime_total: Duration::from_micros(ts.ac_utime),
stime_total: Duration::from_micros(ts.ac_stime),
real_time_total: Duration::from_nanos(ts.cpu_run_real_total),
virtual_time_total: Duration::from_nanos(ts.cpu_run_virtual_total),
},
memory: Memory {
rss_total: ts.coremem,
virt_total: ts.virtmem,
minor_faults: ts.ac_minflt,
major_faults: ts.ac_majflt,
},
io: Io {
read_bytes: ts.read_char,
write_bytes: ts.write_char,
read_syscalls: ts.read_syscalls,
write_syscalls: ts.write_syscalls,
},
blkio: BlkIo {
read_bytes: ts.read_bytes,
write_bytes: ts.write_bytes,
cancelled_write_bytes: ts.cancelled_write_bytes,
},
ctx_switches: ContextSwitches {
voluntary: ts.nvcsw,
non_voluntary: ts.nivcsw,
},
delays: Delays {
cpu: DelayStat {
count: ts.cpu_count,
delay_total: Duration::from_nanos(ts.cpu_delay_total),
},
blkio: DelayStat {
count: ts.blkio_count,
delay_total: Duration::from_nanos(ts.blkio_delay_total),
},
swapin: DelayStat {
count: ts.swapin_count,
delay_total: Duration::from_nanos(ts.swapin_delay_total),
},
freepages: DelayStat {
count: ts.freepages_count,
delay_total: Duration::from_nanos(ts.freepages_delay_total),
},
},
inner_buf,
}
}
}
impl TaskStats {
/// Return inner representation of taskstats.
///
/// The returned value is an instance of `struct taskstats` that was
/// received from kernel.
/// `TaskStats` remaps most of its fields into rust-friendly types and
/// structure, so this inner object should be referred only when the user
/// wants to access more information than available in remapped fields.
pub fn inner(&self) -> &taskstats {
unsafe { &*(self.inner_buf.as_ptr() as *const _ as *const taskstats) }
}
}