1 # -*- coding: utf-8 -*-
2 # Description: cpuidle netdata python.d module
3 # Author: Steven Noonan (tycho)
9 from base import SimpleService
12 syscall = ctypes.CDLL('libc.so.6').syscall
14 # default module values (can be overridden per job in `config`)
17 class Service(SimpleService):
18 def __init__(self, configuration=None, name=None):
19 prefix = os.getenv('NETDATA_HOST_PREFIX', "")
20 if prefix.endswith('/'):
22 self.sys_dir = prefix + "/sys/devices/system/cpu"
23 self.schedstat_path = prefix + "/proc/schedstat"
24 SimpleService.__init__(self, configuration=configuration, name=name)
31 # This is horrendous. We need the *thread id* (not the *process id*),
32 # but there's no Python standard library way of doing that. If you need
33 # to enable this module on a non-x86 machine type, you'll have to find
34 # the Linux syscall number for gettid() and add it to the dictionary
40 if platform.machine() not in syscalls:
42 tid = syscall(syscalls[platform.machine()])
45 def __wake_cpus(self):
46 # Requires Python 3.3+. This will "tickle" each CPU to force it to
47 # update its idle counters.
48 if hasattr(os, 'sched_setaffinity'):
50 save_affinity = os.sched_getaffinity(pid)
51 for idx in range(0, len(self.assignment)):
52 os.sched_setaffinity(pid, [idx])
53 os.sched_getaffinity(pid)
54 os.sched_setaffinity(pid, save_affinity)
56 def __read_schedstat(self):
58 for line in open(self.schedstat_path, 'r'):
59 if not line.startswith('cpu'):
61 line = line.rstrip().split()
64 cpus[cpu] = int(active_time) // 1000
70 # This line is critical for the stats to update. If we don't "tickle"
71 # all the CPUs, then all the counters stop counting.
74 # Use the kernel scheduler stats to determine how much time was spent
76 schedstat = self.__read_schedstat()
78 for cpu, metrics in self.assignment.items():
79 update_time = schedstat[cpu]
80 results[cpu + '_active_time'] = update_time
82 for metric, path in metrics.items():
83 residency = int(open(path, 'r').read())
84 results[metric] = residency
89 if self.__gettid() is None:
90 self.error("Cannot get thread ID. Stats would be completely broken.")
93 self._orig_name = self.chart_name
95 for path in sorted(glob.glob(self.sys_dir + '/cpu*/cpuidle/state*/name')):
96 # ['', 'sys', 'devices', 'system', 'cpu', 'cpu0', 'cpuidle', 'state3', 'name']
97 path_elem = path.split('/')
100 statename = open(path, 'rt').read().rstrip()
102 orderid = '%s_cpuidle' % (cpu,)
103 if orderid not in self.definitions:
104 self.order.append(orderid)
105 active_name = '%s_active_time' % (cpu,)
106 self.definitions[orderid] = {
107 'options': [None, 'C-state residency', 'time%', 'cpuidle', None, 'stacked'],
109 [active_name, 'C0 (active)', 'percentage-of-incremental-row', 1, 1],
112 self.assignment[cpu] = {}
114 defid = '%s_%s_time' % (orderid, state)
116 self.definitions[orderid]['lines'].append(
117 [defid, statename, 'percentage-of-incremental-row', 1, 1]
120 self.assignment[cpu][defid] = '/'.join(path_elem[:-1] + ['time'])
122 # Sort order by kernel-specified CPU index
123 self.order.sort(key=lambda x: int(x.split('_')[0][3:]))
125 if len(self.definitions) == 0:
126 self.error("couldn't find cstate stats")
132 self.chart_name = "cpu"
133 status = SimpleService.create(self)
134 self.chart_name = self._orig_name
137 def update(self, interval):
138 self.chart_name = "cpu"
139 status = SimpleService.update(self, interval=interval)
140 self.chart_name = self._orig_name
143 # vim: set ts=4 sts=4 sw=4 et: