Skip to content

Commit

Permalink
feat(memory): gather and merge memory metrics into Metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
graelo committed Feb 17, 2024
1 parent ea08403 commit c34332c
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 10 deletions.
50 changes: 48 additions & 2 deletions src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
Result,
};

/// Reformulated metrics from the output of the `powermetrics` tool.
/// Reformulated metrics from the output of the `powermetrics` tool and `sysinfo`.
///
/// # Note
///
Expand All @@ -34,6 +34,8 @@ pub(crate) struct Metrics {
pub(crate) consumption: PowerConsumption,
/// Thermal pressure.
pub(crate) thermal_pressure: String,
/// Memory metrics.
pub(crate) memory: MemoryMetrics,
}

impl FromStr for Metrics {
Expand Down Expand Up @@ -61,12 +63,21 @@ impl Metrics {
total
}

/// Merge the Sysinfo CPU metrics with the powermetrics CPU metrics.
pub(crate) fn merge_sysinfo_metrics(
mut self,
sysinfo_metrics: sysinfo::Metrics,
) -> Result<Self> {
self.memory = MemoryMetrics::from(sysinfo_metrics.memory_metrics);
self.set_cpus_active_ratio(&sysinfo_metrics.cpu_metrics)
}

/// Override the CPU active ratio with the values provided by sysinfo.
///
/// Yes this is ugly, but it's the only way to get the correct active ratio given that the
/// powermetrics tool reports incorrect values on M2 chips.
///
pub(crate) fn set_cpus_active_ratio(
pub fn set_cpus_active_ratio(
mut self,
sysinfo_metrics: &[sysinfo::CpuMetrics],
) -> Result<Self> {
Expand Down Expand Up @@ -152,12 +163,15 @@ impl From<plist_parsing::Metrics> for Metrics {
package_w,
};

let memory_metrics = MemoryMetrics::default();

Self {
e_clusters,
p_clusters,
gpu,
consumption,
thermal_pressure: value.thermal_pressure,
memory: memory_metrics,
}
}
}
Expand Down Expand Up @@ -357,6 +371,38 @@ impl From<&str> for ThermalPressure {
}
}

/// Memory metrics: RAM and Swap.
#[derive(Debug, Default, Serialize)]
pub(crate) struct MemoryMetrics {
pub(crate) ram_total: u64,
pub(crate) ram_used: u64,
pub(crate) swap_total: u64,
pub(crate) swap_used: u64,
}

impl MemoryMetrics {
pub(crate) fn ram_usage_ratio(&self) -> f64 {
self.ram_used as f64 / self.ram_total as f64
}
pub(crate) fn swap_usage_ratio(&self) -> f64 {
if self.swap_total == 0 {
return 0.0;
}
self.swap_used as f64 / self.swap_total as f64
}
}

impl From<sysinfo::MemoryMetrics> for MemoryMetrics {
fn from(value: sysinfo::MemoryMetrics) -> Self {
Self {
ram_total: value.ram_total,
ram_used: value.ram_used,
swap_total: value.swap_total,
swap_used: value.swap_used,
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
34 changes: 29 additions & 5 deletions src/modules/sysinfo.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Metrics obtained via the sysinfo crate.
//!
//! Currently, this provides CPU usage per core. This is more accurate than the CPU usage obtained
//! Currently, this provides:
//! - Memory usage
//! - CPU usage per core, which is more accurate than the CPU usage obtained
//! via powermetrics on M2 chips.
use sysinfo::{CpuExt, CpuRefreshKind, RefreshKind, System, SystemExt};
use sysinfo::{CpuRefreshKind, MemoryRefreshKind, System};

pub(crate) struct CpuMetrics {
/// CPU ID (0 - ...)
Expand All @@ -12,8 +14,16 @@ pub(crate) struct CpuMetrics {
pub(crate) active_ratio: f32,
}

pub(crate) struct MemoryMetrics {
pub(crate) ram_total: u64,
pub(crate) ram_used: u64,
pub(crate) swap_total: u64,
pub(crate) swap_used: u64,
}

pub(crate) struct Metrics {
pub(crate) cpu_metrics: Vec<CpuMetrics>,
pub(crate) memory_metrics: MemoryMetrics,
}

pub(crate) struct SystemState {
Expand All @@ -22,15 +32,19 @@ pub(crate) struct SystemState {

impl SystemState {
pub(crate) fn new() -> Self {
let mut system =
System::new_with_specifics(RefreshKind::new().with_cpu(CpuRefreshKind::everything()));
let mut system = System::new();
system.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage());
system.refresh_memory_specifics(MemoryRefreshKind::everything());
Self { system }
}

pub(crate) fn latest_metrics(&mut self) -> Metrics {
self.system
.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage());
self.system
.refresh_memory_specifics(MemoryRefreshKind::new().with_ram());
self.system
.refresh_memory_specifics(MemoryRefreshKind::new().with_swap());

let cpu_metrics = self
.system
Expand All @@ -42,6 +56,16 @@ impl SystemState {
})
.collect();

Metrics { cpu_metrics }
let memory_metrics = MemoryMetrics {
ram_total: self.system.total_memory(), // Note: this never changes.
ram_used: self.system.used_memory(),
swap_total: self.system.total_swap(),
swap_used: self.system.used_swap(),
};

Metrics {
cpu_metrics,
memory_metrics,
}
}
}
6 changes: 3 additions & 3 deletions src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,11 @@ fn stream_metrics(tick_rate: Duration, tx: mpsc::Sender<Event>) {
// of the app.
//
// When the last line of a plist message is read: build the `powermetrics::Metrics` struct and
// gather CPU usage from sysinfo.
// gather CPU usage and Memory from sysinfo.
//
// Finally, send metrics to the event loop.
//
for line in stdout_lines.flatten() {
for line in stdout_lines.map_while(std::result::Result::<String, std::io::Error>::ok) {
if line != "</plist>" {
buffer.append_line(line);
} else {
Expand All @@ -220,7 +220,7 @@ fn stream_metrics(tick_rate: Duration, tx: mpsc::Sender<Event>) {

let sysinfo_metrics = system_state.latest_metrics();

let metrics = match power_metrics.set_cpus_active_ratio(&sysinfo_metrics.cpu_metrics) {
let metrics = match power_metrics.merge_sysinfo_metrics(sysinfo_metrics) {
Ok(metrics) => metrics,
Err(err) => {
eprintln!("{err}");
Expand Down

0 comments on commit c34332c

Please sign in to comment.