use crate::exporters::*;
use crate::sensors::{Record, Sensor, Topology};
use std::collections::HashMap;
use std::thread;
use std::time::{Duration, Instant};
pub struct StdoutExporter {
topology: Topology,
}
impl Exporter for StdoutExporter {
fn run(&mut self, parameters: ArgMatches) {
self.runner(parameters);
}
fn get_options() -> HashMap<String, ExporterOption> {
let mut options = HashMap::new();
options.insert(
String::from("timeout"),
ExporterOption {
default_value: Some(String::from("10")),
long: String::from("timeout"),
short: String::from("t"),
required: false,
takes_value: true,
help: String::from("Maximum time spent measuring, in seconds."),
},
);
options.insert(
String::from("step_duration"),
ExporterOption {
default_value: Some(String::from("2")),
long: String::from("step"),
short: String::from("s"),
required: false,
takes_value: true,
help: String::from("Set measurement step duration in second."),
},
);
options
}
}
impl StdoutExporter {
pub fn new(mut sensor: Box<dyn Sensor>) -> StdoutExporter {
let some_topology = *sensor.get_topology();
StdoutExporter {
topology: some_topology.unwrap(),
}
}
pub fn runner(&mut self, parameters: ArgMatches) {
let timeout = parameters.value_of("timeout").unwrap();
if timeout.is_empty() {
self.iterate();
} else {
let now = Instant::now();
let timeout_secs: u64 = timeout.parse().unwrap();
let step_duration: u64 = parameters
.value_of("step_duration")
.unwrap()
.parse()
.expect("Wrong step_duration value, should be a number of seconds");
println!("Measurement step is: {}s", step_duration);
while now.elapsed().as_secs() <= timeout_secs {
self.iterate();
thread::sleep(Duration::new(step_duration, 0));
}
}
}
fn get_domains_power(&self, socket_id: u16) -> Vec<Option<Record>> {
let socket_present = self
.topology
.get_sockets_passive()
.iter()
.find(move |x| x.id == socket_id);
if let Some(socket) = socket_present {
let mut domains_power: Vec<Option<Record>> = vec![];
for d in socket.get_domains_passive() {
domains_power.push(d.get_records_diff_power_microwatts());
}
domains_power
} else {
vec![None, None, None]
}
}
fn iterate(&mut self) {
self.topology.refresh();
self.show_metrics();
}
fn show_metrics(&self) {
let host_power = match self.topology.get_records_diff_power_microwatts() {
Some(record) => record.value.parse::<u64>().unwrap(),
None => 0,
};
let mut sockets_power: HashMap<u16, (u64, Vec<Option<Record>>)> = HashMap::new();
let sockets = self.topology.get_sockets_passive();
for s in sockets {
let socket_power = match s.get_records_diff_power_microwatts() {
Some(record) => record.value.parse::<u64>().unwrap(),
None => 0,
};
sockets_power.insert(s.id, (socket_power, self.get_domains_power(s.id)));
}
println!(
"Host:\t{} W\tCore\t\tUncore\t\tDRAM",
(host_power as f32 / 1000000.0)
);
for (s_id, v) in sockets_power.iter() {
let power = (v.0 as f32) / 1000000.0;
let mut power_str = String::from('?');
if power > 0.0 {
power_str = power.to_string();
}
let mut to_print = format!("Socket{}\t{} W\t", s_id, power_str);
for d in v.1.iter() {
let domain_power = match d {
Some(record) => record.value.parse::<u64>().unwrap(),
None => 0,
};
to_print.push_str(&format!("{} W\t", domain_power as f32 / 1000000.0));
}
println!("{}", to_print);
}
println!("Top 5 consumers:");
println!("Power\tPID\tExe");
let consumers = self.topology.proc_tracker.get_top_consumers(5);
for c in consumers.iter() {
if let Some(host_stat) = self.topology.get_stats_diff() {
let host_time = host_stat.total_time_jiffies();
println!(
"{} W\t{}\t{:?}",
((c.1 as f32 / (host_time * procfs::ticks_per_second().unwrap() as f32))
* host_power as f32)
/ 1000000.0,
c.0.pid,
c.0.exe().unwrap_or_default()
);
}
}
println!("------------------------------------------------------------\n");
}
}
#[cfg(test)]
mod tests {
}