]> arthur.barton.de Git - netdata.git/blobdiff - python.d/cpufreq.chart.py
Merge branch 'master' into ab-debian
[netdata.git] / python.d / cpufreq.chart.py
index 600786bfeeefd85454fb80b5e90db03041d604ae..e28bdea8f6abeba09752edda468075cebca779d4 100644 (file)
@@ -1,8 +1,10 @@
 # -*- coding: utf-8 -*-
 # Description: cpufreq netdata python.d module
-# Author: Pawel Krupa (paulfantom)
+# Author: Pawel Krupa (paulfantom) and Steven Noonan (tycho)
 
+import glob
 import os
+import time
 from base import SimpleService
 
 # default module values (can be overridden per job in `config`)
@@ -18,26 +20,54 @@ CHARTS = {
         ]}
 }
 
-
 class Service(SimpleService):
     def __init__(self, configuration=None, name=None):
-        self.sys_dir = "/sys/devices"
-        self.filename = "scaling_cur_freq"
+        prefix = os.getenv('NETDATA_HOST_PREFIX', "")
+        if prefix.endswith('/'):
+            prefix = prefix[:-1]
+        self.sys_dir = prefix + "/sys/devices"
         SimpleService.__init__(self, configuration=configuration, name=name)
         self.order = ORDER
         self.definitions = CHARTS
         self._orig_name = ""
         self.assignment = {}
-        self.paths = []
+        self.accurate_exists = True
+        self.accurate_last = {}
 
     def _get_data(self):
-        raw = {}
-        for path in self.paths:
-            with open(path, 'r') as f:
-                raw[path] = f.read()
         data = {}
-        for path in self.paths:
-            data[self.assignment[path]] = raw[path]
+
+        if self.accurate_exists:
+            elapsed = time.time() - self.timetable['last']
+
+            accurate_ok = True
+
+            for name, paths in self.assignment.items():
+                last = self.accurate_last[name]
+                current = 0
+                for line in open(paths['accurate'], 'r'):
+                    line = list(map(int, line.split()))
+                    current += (line[0] * line[1]) / 100
+                delta = current - last
+                data[name] = delta
+                self.accurate_last[name] = current
+                if delta == 0 or abs(delta) > 1e7:
+                    # Delta is either too large or nonexistent, fall back to
+                    # less accurate reading. This can happen if we switch
+                    # to/from the 'schedutil' governor, which doesn't report
+                    # stats.
+                    accurate_ok = False
+
+            if accurate_ok:
+                return data
+            else:
+                self.alert("accurate method failed, falling back")
+                self.accurate_exists = False
+
+
+        for name, paths in self.assignment.items():
+            data[name] = open(paths['inaccurate'], 'r').read()
+
         return data
 
     def check(self):
@@ -48,23 +78,30 @@ class Service(SimpleService):
 
         self._orig_name = self.chart_name
 
-        for dirpath, _, filenames in os.walk(self.sys_dir):
-            if self.filename in filenames:
-                self.paths.append(dirpath + "/" + self.filename)
-
-        if len(self.paths) == 0:
-            self.error("cannot find", self.filename)
+        for path in glob.glob(self.sys_dir + '/system/cpu/cpu*/cpufreq/stats/time_in_state'):
+            path_elem = path.split('/')
+            cpu = path_elem[-4]
+            if cpu not in self.assignment:
+                self.assignment[cpu] = {}
+            self.assignment[cpu]['accurate'] = path
+            self.accurate_last[cpu] = 0
+
+        if len(self.assignment) == 0:
+            self.accurate_exists = False
+
+        for path in glob.glob(self.sys_dir + '/system/cpu/cpu*/cpufreq/scaling_cur_freq'):
+            path_elem = path.split('/')
+            cpu = path_elem[-3]
+            if cpu not in self.assignment:
+                self.assignment[cpu] = {}
+            self.assignment[cpu]['inaccurate'] = path
+
+        if len(self.assignment) == 0:
+            self.error("couldn't find a method to read cpufreq statistics")
             return False
 
-        self.paths.sort()
-        i = 0
-        for path in self.paths:
-            self.assignment[path] = "cpu" + str(i)
-            i += 1
-
-        for name in self.assignment:
-            dim = self.assignment[name]
-            self.definitions[ORDER[0]]['lines'].append([dim, dim, 'absolute', 1, 1000])
+        for name in self.assignment.keys():
+            self.definitions[ORDER[0]]['lines'].append([name, name, 'absolute', 1, 1000])
 
         return True