@cryptotaxi247 / netdata-1 / commits / c290b78e7

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