changeset 28:e5c831894653

Add CPU load metric
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 16 Feb 2016 21:37:02 +0100
parents 2e623c2fb4f1
children 974108b35b11
files src/main.rs src/metrics/cpu_load.rs src/metrics/mod.rs
diffstat 3 files changed, 130 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/main.rs	Tue Feb 16 21:36:46 2016 +0100
+++ b/src/main.rs	Tue Feb 16 21:37:02 2016 +0100
@@ -74,7 +74,7 @@
     /// Returns a map of metric -> position in ordering list
     fn make_ordering_map(ord_list: String) -> BTreeMap<String, i32> {
         let parts = ord_list.split(",");
-        let mut i = 0;
+        let mut i = 1;
         let mut ordmap = BTreeMap::new();
 
         for metric in parts {
@@ -120,6 +120,7 @@
 }
 
 fn register_metrics(registry: &mut AvailableMetrics) {
+    use metrics::cpu_load;
     use metrics::load;
     use metrics::net;
     use metrics::time;
@@ -138,6 +139,12 @@
                               minutes.",
                              "",
                              load::make_load_metric());
+    registry.register_metric("cpu_load",
+                             "Shows the cpu load in percent over the last measure interval. abs \
+                              means: 4 core seconds = 400%; rel means (on a quadcore): 4 core \
+                              seconds = 100%",
+                             "abs|rel",
+                             cpu_load::make_cpu_load_metric());
 }
 
 fn main() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/metrics/cpu_load.rs	Tue Feb 16 21:37:02 2016 +0100
@@ -0,0 +1,121 @@
+use framework::*;
+use helper::get_procfs_file_lines;
+
+extern crate regex;
+use self::regex::Regex;
+
+extern crate libc;
+
+#[derive(PartialEq)]
+enum DisplayMode {
+    Absolute,
+    Relative,
+}
+
+struct CPULoadMetric {
+    mode: DisplayMode,
+    ncpu: i32,
+    user_hz: u64,
+    last_cpu_millis: u64,
+
+    individual_cpu_regex: Regex,
+    total_cpu_regex: Regex,
+}
+
+impl CPULoadMetric {
+    fn new() -> CPULoadMetric {
+        use self::libc::{sysconf, _SC_CLK_TCK};
+
+        CPULoadMetric {
+            mode: DisplayMode::Absolute,
+            ncpu: 1,
+            user_hz: unsafe { sysconf(_SC_CLK_TCK) } as u64,
+            last_cpu_millis: 0,
+            individual_cpu_regex: Regex::new(r"^cpu(\d+)\s+").unwrap(),
+            // user nice system
+            total_cpu_regex: Regex::new(r"^cpu\s+(\d+)\s+(\d+)\s+(\d+)").unwrap(),
+        }
+    }
+    fn get_number_of_cores(&self) -> i32 {
+        let lines = get_procfs_file_lines(String::from("/stat"));
+
+        match lines {
+            None => 1,
+            Some(lns) => {
+                let mut n = 0;
+                for line in lns {
+                    if self.individual_cpu_regex.is_match(&line) {
+                        n += 1;
+                    } else if n > 0 {
+                        break;
+                    }
+                }
+                n
+            }
+        }
+    }
+    fn get_total_cpu_millis(&self) -> u64 {
+
+        let lines = get_procfs_file_lines(String::from("/stat"));
+
+        match lines {
+            None => return 0,
+            Some(lns) => {
+                for line in lns {
+                    match self.total_cpu_regex.captures(&line) {
+                        None => return 0,
+                        Some(cpts) => {
+                            return self.calc_total_cpu_millis(cpts.at(1).unwrap_or("0"),
+                                                              cpts.at(2).unwrap_or("0"),
+                                                              cpts.at(3).unwrap_or("0"))
+                        }
+                    }
+                }
+            }
+        }
+        return 0;
+    }
+
+    fn calc_total_cpu_millis(&self, user: &str, nice: &str, sys: &str) -> u64 {
+        use std::str::FromStr;
+        return (1000 / self.user_hz) *
+               (u64::from_str(user).unwrap_or(0) + u64::from_str(nice).unwrap_or(0) +
+                u64::from_str(sys).unwrap_or(0));
+    }
+}
+
+impl Metric for CPULoadMetric {
+    // arg can be "abs" or "rel" (default is 'abs')
+    // "abs" means that a fully loaded CPU has a load of #cores * 100%
+    // "rel" means that a fully loaded CPU has a load of 100%.
+    fn init(&mut self, _: &mut MetricState, arg: Option<String>) {
+        match arg {
+            None => (),
+            Some(s) => {
+                if s == "rel" {
+                    self.mode = DisplayMode::Relative;
+                }
+            }
+        }
+        self.ncpu = self.get_number_of_cores();
+    }
+    fn render(&mut self, st: &mut MetricState) -> RenderResult {
+        // evaluation interval in milliseconds
+        let interval = MetricState::now() - st.last_called;
+        let current_time = self.get_total_cpu_millis();
+        let diff = current_time - self.last_cpu_millis;
+        self.last_cpu_millis = current_time;
+
+        let mut percentage = 100f64 * (diff as f64 / interval as f64);
+
+        if self.mode == DisplayMode::Relative {
+            percentage /= self.ncpu as f64;
+        }
+
+        RenderResult::new(format!("{:4.0}%", percentage), Color::Default)
+    }
+}
+
+pub fn make_cpu_load_metric() -> Box<Metric> {
+    Box::new(CPULoadMetric::new())
+}
--- a/src/metrics/mod.rs	Tue Feb 16 21:36:46 2016 +0100
+++ b/src/metrics/mod.rs	Tue Feb 16 21:37:02 2016 +0100
@@ -1,3 +1,4 @@
+pub mod cpu_load;
 pub mod load;
 pub mod net;
 pub mod time;