cpufreq.chart.py: support dynamically falling back from accurate to inaccurate mode
If we switch the 'schedutil' cpufreq governor on, the 'time_in_state' data becomes useless. And since cpufreq governors can be switched at any time, we might as well make it possible to support transitioning between governors without any bogus data appearing in our charts. Before this commit, switching to 'schedutil' results in zeroed values, and switching back to any other governor causes a huge spike datapoint in the timeline. Signed-off-by: Steven Noonan <steven@uplinklabs.net>
Steven Noonan committed
Jan 10, 2017 at 04:46 UTC
c290b78e7ea07632c3350068c53a66c5392b8313
1 file changed
+45
-27
python.d/cpufreq.chart.py
+45
-27
@@ -4,6 +4,7 @@
4
5
import glob
6
import os
7
+import time
8
from base import SimpleService
9
10
# default module values (can be overridden per job in `config`)
@@ -30,20 +31,42 @@ class Service(SimpleService):
31
self.definitions = CHARTS
32
self._orig_name = ""
33
self.assignment = {}
33
- self.accurate = True
34
+ self.accurate_exists = True
35
+ self.accurate_last = {}
36
37
def _get_data(self):
38
data = {}
37
- if self.accurate:
38
- for name, path in self.assignment.items():
39
- total = 0
40
- for line in open(path, 'r'):
39
+
40
+ if self.accurate_exists:
41
+ elapsed = time.time() - self.timetable['last']
42
+
43
+ accurate_ok = True
44
+
45
+ for name, paths in self.assignment.items():
46
+ last = self.accurate_last[name]
47
+ current = 0
48
+ for line in open(paths['accurate'], 'r'):
49
line = list(map(int, line.split()))
42
- total += (line[0] * line[1]) / 100
43
- data[name] = total
44
- else:
45
- for name, path in self.assignment.items():
46
- data[name] = open(path, 'r').read()
50
+ current += (line[0] * line[1]) / 100
51
+ delta = current - last
52
+ data[name] = delta
53
+ self.accurate_last[name] = current
54
+ if delta == 0 or abs(delta) > 1e7:
55
+ # Delta is either too large or nonexistent, fall back to
56
+ # less accurate reading. This can happen if we switch
57
+ # to/from the 'schedutil' governor, which doesn't report
58
+ # stats.
59
+ accurate_ok = False
60
+
61
+ if accurate_ok:
62
+ return data
63
+ else:
64
+ self.alert("accurate method failed, falling back")
65
+
66
+
67
+ for name, paths in self.assignment.items():
68
+ data[name] = open(paths['inaccurate'], 'r').read()
69
+
70
return data
71
72
def check(self):
@@ -55,34 +78,29 @@ class Service(SimpleService):
78
self._orig_name = self.chart_name
79
80
for path in glob.glob(self.sys_dir + '/system/cpu/cpu*/cpufreq/stats/time_in_state'):
58
- if len(open(path, 'rb').read().rstrip()) == 0:
59
- self.alert("time_in_state is empty, broken cpufreq_stats data")
60
- self.assignment = {}
61
- break
81
path_elem = path.split('/')
82
cpu = path_elem[-4]
64
- self.assignment[cpu] = path
83
+ if cpu not in self.assignment:
84
+ self.assignment[cpu] = {}
85
+ self.assignment[cpu]['accurate'] = path
86
+ self.accurate_last[cpu] = 0
87
88
if len(self.assignment) == 0:
67
- self.alert("trying less accurate scaling_cur_freq method")
68
- self.accurate = False
89
+ self.accurate_exists = False
90
70
- for path in glob.glob(self.sys_dir + '/system/cpu/cpu*/cpufreq/scaling_cur_freq'):
71
- path_elem = path.split('/')
72
- cpu = path_elem[-3]
73
- self.assignment[cpu] = path
91
+ for path in glob.glob(self.sys_dir + '/system/cpu/cpu*/cpufreq/scaling_cur_freq'):
92
+ path_elem = path.split('/')
93
+ cpu = path_elem[-3]
94
+ if cpu not in self.assignment:
95
+ self.assignment[cpu] = {}
96
+ self.assignment[cpu]['inaccurate'] = path
97
98
if len(self.assignment) == 0:
99
self.error("couldn't find a method to read cpufreq statistics")
100
return False
101
79
- if self.accurate:
80
- algo = 'incremental'
81
- else:
82
- algo = 'absolute'
83
-
102
for name in self.assignment.keys():
85
- self.definitions[ORDER[0]]['lines'].append([name, name, algo, 1, 1000])
103
+ self.definitions[ORDER[0]]['lines'].append([name, name, 'absolute', 1, 1000])
104
105
return True
106